小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

深入理解synchronized

 貪挽懶月 2022-06-20 發(fā)布于廣東

面試官: 請談?wù)勀銓ynchronized的理解。

小白: 這是一個java的關(guān)鍵字,用來控制并發(fā)的,被它鎖住的代碼同一時刻只能有一個線程訪問。

面試官: 還有嗎?

小白: 沒有了……

面試官: 那你先回去等通知吧!


synchronized,相信學(xué)過java的都知道它,但是面試一被問到這個,又總是答不出多少東西來。下面我就將synchronized的知識點列舉出來,深入理解(要深入它,才能征服它)。


1. 用來干嘛的?

這是一個同步關(guān)鍵字,保證同一時刻只能有一個線程執(zhí)行被其修飾的方法或代碼塊,可以保證線程安全。

2. 怎么用呢?

這是一個關(guān)鍵字,可以用來修飾靜態(tài)方法、實例方法、代碼塊。注意這里的代碼塊不是類中的靜態(tài)代碼塊和構(gòu)造代碼塊,而是方法中的代碼塊。

  • 修飾靜態(tài)方法:介紹它修飾靜態(tài)方法之前,先來回憶一下靜態(tài)方法的特點。靜態(tài)是該類所有實例共享的,JVM加載該類時就會對其進行初始化,因為不屬于任何一個實例,所以靜態(tài)方法里面不能用this關(guān)鍵字。如果synchronized修飾靜態(tài)方法,那么鎖對象是啥呢?首先排除this,因為調(diào)用靜態(tài)方法的時候可能該類都還沒有實例。所以修飾靜態(tài)方法的時候,鎖對象其實是當前class。
// 靜態(tài)方法
public synchronized static void staticFun(){
    System.out.println("synchronized修飾靜態(tài)方法,鎖對象是當前class");
    // 業(yè)務(wù)代碼……
}
  • 修飾實例方法:既然都說了是實例方法,那么鎖對象就是當前類的實例。
// 實例方法
public synchronized void instanceFun(){
    System.out.println("synchronized修飾實例方法,鎖對象是類實例");
    // 業(yè)務(wù)代碼……
}
  • 修飾代碼塊:修飾代碼塊,鎖對象可以是class,也可以是給定的對象。如果是class,那就是不管該類new幾個實例,都是屬于這個類的,都會被鎖??;如果是對象,那么不同對象去訪問時是可以獲取到鎖的,所以class作為鎖其實粒度更粗。
public void fun(){
    synchronized (TestSync.class){ // 鎖對象是當前class
//    synchronized (this){ // 鎖對象是實例
        System.out.println("synchronized修飾代碼塊,鎖對象可以是實例,可以是類");
    }
}

3. 線程A調(diào)用類的同步實例方法,線程B可以同時調(diào)用類的同步靜態(tài)方法嗎?為什么?

我們先用代碼看結(jié)果,再解釋為什么。

 // 靜態(tài)方法
 public synchronized static void staticFun(){
     System.out.println("synchronized修飾靜態(tài)方法,鎖對象是當前class");
     System.out.println(Thread.currentThread().getName() + "進入同步靜態(tài)方法");
     System.out.println(Thread.currentThread().getName() + "執(zhí)行結(jié)束");
 }

 // 實例方法
 public synchronized void instanceFun(){
     System.out.println("synchronized修飾實例方法,鎖對象是類實例");
     System.out.println(Thread.currentThread().getName() + "進入同步實例方法");
     try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
     System.out.println(Thread.currentThread().getName() + "執(zhí)行結(jié)束");
 }

 public static void main(String[] args){
     TestSync testSync = new TestSync();
     new Thread(() -> {
         testSync.instanceFun();
     }, "線程A").start();

     new Thread(() -> {
         staticFun();
     }, "線程B").start();

     new Thread(() -> {
         testSync.instanceFun();
     }, "線程C").start();
 }

運行結(jié)果:

運行結(jié)果

上面的代碼,線程A調(diào)用實例方法,并且進入方法后線程睡了5秒鐘;線程B調(diào)用靜態(tài)方法,還沒等線程A結(jié)束,線程B已經(jīng)執(zhí)行結(jié)束了,線程B不需要等線程A釋放鎖也可以執(zhí)行。而線程C,因為是同一個對象去調(diào)用的同步實例方法,所以得等線程A釋放了鎖,線程C才能拿到執(zhí)行權(quán)。假如線程C是另外再new一個對象去調(diào)用的,那么也不需要等待線程A釋放鎖。

從結(jié)果可以得出答案:線程A調(diào)用類的同步實例方法,線程B可以同時調(diào)用類的同步靜態(tài)方法。原因就是同步實例方法的鎖是對象鎖,而同步靜態(tài)方法的鎖是類鎖,鎖對象不同,所以可以同時調(diào)用

4. 可以用String字符串來做鎖對象嗎?

可以,但沒必要。代碼塊的鎖對象其實可以是任意對象,不過一般都用class或者this,并不建議用string做鎖對象,因為用string很容易造成死鎖。為什么容易造成死鎖呢?因為JVM中有個常量池,比如你定義兩個字符串:

String str1 = "haha";
String str2 = "haha";

這里明明是兩個字符串,但其實是同一個對象,因為這樣賦值的String,首先會看常量池中有沒有,沒有就往常量池中添加一個,并指向它,有的話,就直接指向。所以str1和str2都是指向常量池中同一個對象。

5. synchronized可以修飾構(gòu)造方法嗎?為什么?

不能修飾構(gòu)造方法,構(gòu)造方法只能有權(quán)限修飾符,比如public、private之類的,它本身就是線程安全的。

6. jdk1.6開始對synchronized做了哪些優(yōu)化?

jdk1.6之前,synchronized是很重的鎖,jdk1.6開始,做了大量的優(yōu)化,比如用偏向鎖、輕量級鎖、自旋鎖、適應(yīng)性自旋鎖、鎖消除、鎖粗化等技術(shù)來減少鎖操作的開銷。

7. 你知道synchronized的底層原理嗎?

  • 同步代碼塊:方法里面的同步代碼塊,synchronized底層是通過監(jiān)視器monitor來實現(xiàn)的。通過指令
javap -c -s -v -l Xxx.class

可以發(fā)現(xiàn)monitorenter指令指向同步代碼塊開始的位置,同時會嘗試獲取鎖,鎖的計數(shù)器為0表示可以獲取鎖,獲取后計數(shù)器變?yōu)?;monitorexit指令指向同步代碼塊結(jié)束的位置,同時釋放鎖,將鎖的計數(shù)器置為0。所以獲取鎖就是獲取Monitor的執(zhí)行權(quán)。Monitor是基于C++,由ObjectMonitor實現(xiàn)的,每個對象都內(nèi)置了ObjectMonitor。另外,wait/notify方法也是基于monitor來實現(xiàn)的。

  • 同步方法:執(zhí)行上述的javap指令查看同步方法,可以發(fā)現(xiàn)并沒有monitorenter和monitorexit指令,但是在方法開頭有個名為ACC_SYNCHRONIZED的flag標識,同步方法就是通過這個標識來控制同步操作的。

8. synchronized和ReentrantLock有何異同?

相同點:

  • 兩者都是可重入鎖;

  • 都可實現(xiàn)選擇性通知;

不同點:

  • synchronized是JVM層面的,ReentrantLock是API層面的;

  • synchronized是非公平鎖,ReentrantLock可以指定為公平鎖或者非公平鎖;

  • synchronized無需手動釋放鎖,ReentrantLock需要手動釋放鎖;

  • synchronized等待不能中斷,ReentrantLock等待可通過lock.lockInterruptibly()中斷等待;


掃描二維碼

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多