1. 程式人生 > >Java synchronized併發同步詳細使用操作技巧-類鎖、物件鎖、this鎖、非this鎖

Java synchronized併發同步詳細使用操作技巧-類鎖、物件鎖、this鎖、非this鎖

synchronized基礎特點:

 

synchronized鎖是可重入的,且在父子類繼承中同樣適用;

synchronized鎖在遇到異常時自動釋放鎖;

synchronized鎖的同步化不可以繼承;

 

1、非執行緒安全的高發區

 

1.1、 多執行緒下的全域性變數

1.2、多執行緒下的未同步的方法

1.3、多執行緒下的未進行合理同步的方法

 

本篇主要介紹synchronized的使用技巧,主要針對的情況是1.2和1.3。

 

要點:只有共享資源的讀寫(比如java的讀寫鎖,僅讀讀不需要獨佔同步,讀寫,寫寫,寫讀均需要獨佔式同步)操作才需要同步化,如果不是共享資源,則不需要進行多餘的同步,這也涉及到鎖的粒度問題,後面我再細聊。

 

synchronized是java中使用的較多的同步關鍵字,且一直被不斷優化。使用synchronized較為簡單,不像Lock那樣需要手動釋放鎖,雖然失去了一定的靈活性,但對大多數情況下,synchronized還是明智的選擇。下文會著重介紹synchronized的使用技巧。

 

2、synchronized修飾物件方法

 

2.1、當多個執行緒同時訪問同一個物件的同步方法時,是執行緒安全的,注意一定是同一個物件,如果建立下圖中的兩個MyObject類並分別在兩個執行緒的run方法中呼叫methodA方法,就是非執行緒安全的了,因為synchronized關鍵字獲取的為物件鎖。

如下圖所示:

2.2、在同一個例項物件中,synchronized關鍵字僅對加了synchronized關鍵字的方法)會保證執行緒安全,而對同一個例項物件中的未加synchronized關鍵字方法不保證執行緒安全。

如下圖所示:

注:在多執行緒情況下,同一個MyObject物件的methodA方法是執行緒安全的,而methodB則是非執行緒安全的。

 

2.3、在同一個例項物件中,如果多個方法都被synchronized關鍵字修飾,則該例項物件中的多個方法之間是同步的,需要等待上一個方法執行結束後下一個獲得該物件鎖的方法才可以執行,這也正說明了synchronized關鍵字獲得是物件鎖,多個方法會爭奪同一個物件鎖。

如下圖所示:

注:方法A和方法B的執行是有序的,先獲得物件鎖的方法先執行,另一個方法會在該方法執行完畢再執行。

 

3、synchronized修飾靜態方法

 

synchronized修飾靜態方法時,持有的鎖為類鎖,在這種情況下,無論建立多少物件,都共同持有同一個鎖即類鎖。

如下圖所示:

3.1、兩個靜態方法同時加synchronized時,會發生阻塞。

如下圖所示: 

3.2、一個靜態方法一個動態方法,同時加synchronized,不會發生阻塞,原因是一個是靜態方法的synchronized持有的是類鎖,動態方法持有的synchronized是物件鎖。

如下圖所示:

3.3、同步synchronized(class)和直接使用synchronized修飾靜態方法效果一樣,都是獲得類鎖,而不是物件鎖。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

 

4、synchronized結合靜態內部類

 

4.1、synchronized(this)的情況下

 

當多個併發執行緒訪問同一個物件的synchronized(this)同步程式碼塊時,各個執行緒是依次執行的,即該物件的同步程式碼塊中的部分是執行緒同步的。synchronized同步程式碼塊提升一定的效率。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

結合上圖中的程式碼,當一個執行緒訪問該物件的synchronized同步程式碼塊時,其他執行緒仍然可以訪問該物件非synchronized同步程式碼塊中的內容。且能符合我們對執行緒同步的預期,即synchronized同步程式碼塊持有當前呼叫物件的鎖,且為同步的。

 

注:當一個執行緒訪問該物件的一個synchronized(this)同步程式碼塊時,其他執行緒對該物件所有其他的synchronized(this)同步程式碼塊的訪問將被阻塞,因為synchronized(this)中的this獲取的是整個物件的物件鎖。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

方法1和方法2會依次執行。同理,下圖中的三個方法也是依次執行的:方法3為synchronized修飾的整個方法,而方法1和方法2為synchronized(this)同步程式碼塊。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

4.2、synchronized(非this)的情況下

 

使用synchronized(非this)同步程式碼塊時,同一個例項物件可以有多個synchronized(非this)同步程式碼塊,當synchronized(非this)中非this物件相同時,多個synchronized(非this)程式碼塊中的程式碼具有阻塞性,當非this物件不同時,多個synchronized(非this)程式碼塊分別具有同步特性,相互之間不會出現阻塞。

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

下圖中兩個synchronized塊持有不同的鎖,故不會發生阻塞,方法A持有anString鎖,方法B持有anString2鎖;

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

下圖中第一個方法持有該物件的物件鎖,第二個方法持有anString鎖;

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

注:

4.3、當多個執行緒同時執行synchronized(x){}同步程式碼塊時,呈同步效果,各個synchronized(x){}塊會發生阻塞;

如下圖所示:

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

4.4、當其他執行緒執行x物件中的synchronized同步方法或synchronized(this)同步程式碼塊時,呈同步效果。

如下三個圖所示:

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

示例圖已放公眾號-up隨想,此處未重複新增,歡迎搜尋公眾號up隨想或掃描下方二維碼關注檢視:

<

4.5、如果其他執行緒呼叫未加synchronized的方法時,不會發生同步。

 

注:字串一般不用於synchronized的鎖物件,因為string常量池會產生出乎意料的鎖同步和阻塞現象。另外如果鎖物件放入快取或者map後,如果被另一個synchronized所持有,也會發生同步或者阻塞。

5 、synchronized結合內部類

本質上和上述一致,只是將判斷鎖物件以及使用synchronized位置變換了下而已,只要找到正確的鎖物件以及共享資料,就可以輕易的對同步是否合理及是否需要改進進行判斷。

 

參考:java併發程式設計實踐、java多執行緒核心程式設計技術,java併發程式設計的藝術等書,

 

歡迎大家關注我的公眾號號 “up隨想”!!

 

本篇完。持續更新完善中......關注有驚喜,共同學習