一文搞懂Synchronized

簡介:對多線程可見,保證同一時刻最多隻有一個線程執行代碼,達到線程併發安全,被修飾的代碼保證了可見性、一定程度上的原子性和禁止指令重排序。關鍵是獨佔一個鎖。關鍵字,java原生支持,最基本的互斥同步手段。

如果不使用併發手段的後果:比如兩個線程同時執行a++,最後結果比預期的要少

原因:a++包含三個操作不具有原子性,讀取a值,a+1,然後寫入內存,共分三個步驟搶佔式調度引起,底層原理是java內存模型機制引起。

用法:

對象鎖:方法鎖(默認對象爲this當前實例對象)和同步代碼塊鎖(自己指定鎖對象包括this)

類鎖:指Synchrinized修飾的靜態方法或者代碼塊指定鎖爲Class對象,java類可以用很多對象,只有一個Class對象

鎖不同相互沒有影響--並行而非串行

類鎖可以全局保護,不同的對象實例進來也可以同步執行如New Thrend(class.this1)和

New Thrend(class.this2),同時執行同步代碼塊,對象鎖是並行的而類鎖可以同步執行

 

多線程訪問同步方法的七種情況

1:兩個線程同時訪問一個對象的同步方法

       同一把鎖存在阻塞

2:兩個線程訪問來兩個對象的同步方法

       鎖住不同的實例,所以不會阻塞

3:兩個線程訪問的是靜態方法

       鎖對象爲Class對象,存在阻塞

4:如果同時訪問同步方法和非同步方法

       非同步方法不會受到影響

5:訪問同一個對象的不同的普通同步方法

      鎖是一樣的,發生阻塞

6:同時訪問靜態和非靜態的同步方法

      兩個鎖不一樣,不受影響,串行

7:方法拋出異常,會釋放鎖

      總結:

          a:一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待

          b:每一個實例都對應有自己的一把鎖,不同的實例之間相互不影響,但是Class對象鎖,所有對象公用一把鎖

          c:無論正常執行完畢還是拋出異常都會釋放鎖

Synchrinized性質:

   1.可重入(遞歸鎖):同一線程外層函數獲得鎖之後,內層函數可以直接再獲取該鎖,避免死鎖,提升封裝性,粒度:線程而非調用

  2. 獨佔

    可重入性質:

      1:   同一個方法可重入

      2:可重入不要求是同一個方法

      3:可重入不要求是同一個類

  不可中斷:一旦鎖已經被別人獲得,,如果我想再想獲得,就只能等待或者阻塞,直到別的線程釋放鎖,否則會一直等待下去(與Lock比,可以設置超時時間)

                                                                   原理

1:加鎖和釋放鎖

現象

     內置鎖

     等價代碼----Lock

     深入JVM字節碼:

                        概況:java對象頭存儲synchrinized,基於monitor

                        反編譯:monitorenter開始鎖住代碼塊,monitorexit釋放鎖

                        Monitorenter:+1獲取鎖,重入再+1,其他線程阻塞

                        Monitorexit:計數器-1,爲0就失去monitor的所有權,其他線程可以獲取可以獲取鎖

2:可重入原理:每一個對象都有一把瑣,jvm負責跟蹤對象被加鎖的次數

              線程第一次給對象加鎖的時候,計數器變爲1,每當相同的線程此對象上再次獲得鎖,計數器會遞增

              每當任務離開的時候,計數遞減,每當計數爲0的時候,鎖被完全釋放

3:保證可見性原理:

                Java內存模型:1:主內存數據複製副本到工作內存,2更新,3更新後把數據寫入主內存中,一旦被synchrinized修飾,同時只有一個線程修改數據,所以修改對其他線程是可見的,保證線程安全性

缺陷:

           效率低:所得釋放情況少,只有當前線程執行結束和發生異常才釋放鎖,試圖獲得時不能設定超時時間,不能中斷一個正在試圖獲得鎖的線程---Lock對比

           不夠靈活(讀寫鎖更加靈活):加鎖和釋鎖的時機單一,每個鎖僅有單一的條件(某個對象),可能是不夠的

           無法知道是否成功的獲取了鎖                                                        

 ---Lock接口對比

           Lock lock =new reenetrylock

           lock.lock()

           lock.unLock()

           lock.tryLock() ----返回boobean

           lock.trylock(超時時間)----返回boobean

思考:

       1:使用注意點:鎖對象不能爲空、作用域不宜過大(效率低)、避免死鎖

       2:如何選擇sync和Lock?

            建議:如果可以都不使用,使用併發包中的類,考慮開發效率,出錯機率以及執行效率

       3:多線程訪問同步方法的各種具體情況

思考:

1:多個線程等待同一個synchronized鎖的時候,JVM如何選擇下一個獲取鎖的線程,涉及鎖的調度機制

2:sync使得同時只有一個線程可以執行,性能差,如何提高性能,優化鎖的範圍儘可能小,

可以替代,比如讀寫鎖

3:更加靈活的控制鎖的獲取和釋放?

4:什麼是鎖的升級、降級?什麼是JVM裏的輕量級鎖、偏斜鎖、重量級鎖?  

總結:

一句話介紹synchronized:

            Jvm會自動通過使用monitro來加鎖和解鎖,使鎖住的代碼塊具有原子性操縱,保證只有一個線程可以執行指定的代碼,從而保證線程安全,同時具有可重入和不可中斷的性質   

ps:寫的不夠好,如果有不足或者錯誤的地方歡迎留言指正  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章