Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

synchronized基礎特點:

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

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

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

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等


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關鍵字獲取的為對象鎖。

如下面圖1所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖1

2.2、在同一個實例對象中,synchronized關鍵字僅對加了synchronized關鍵字的方法)會保證線程安全,而對同一個實例對象中的未加synchronized關鍵字方法不保證線程安全。

如下面圖2所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖2

注:在多線程情況下,同一個MyObject對象的methodA方法是線程安全的,而methodB則是非線程安全的。

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

如下面圖3所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖3

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

3、synchronized修飾靜態方法

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

如下面圖4所示:


Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖4

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

如下面圖5所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖5

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

如下面圖6所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖6

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

如下面圖7所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖7

4、synchronized結合靜態內部類

4.1、synchronized(this)的情況下

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

如下面圖8所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖8

結合上面圖8中的代碼,當一個線程訪問該對象的synchronized同步代碼塊時,其他線程仍然可以訪問該對象非synchronized同步代碼塊中的內容。且能符合我們對線程同步的預期,即synchronized同步代碼塊持有當前調用對象的鎖,且為同步的。

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

如下面圖9所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖9

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

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖10

4.2、synchronized(非this)的情況下

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

如下面圖11所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖11

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

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖12

下面圖13中第一個方法持有該對象的對象鎖,第二個方法持有anString鎖;

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖13

注:

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

如下面圖14所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖14

4.4、當其他線程執行x對象中的synchronized同步方法或synchronized(this)同步代碼塊時,呈同步效果。

如下圖15、圖16、圖17所示:

Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖15


Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖16


Java synchronized 各種操作要點-對象鎖類鎖this鎖非this鎖等

圖17

4.5、如果其他線程調用未加synchronized的方法時,不會發生同步。

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

5 、synchronized結合內部類

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

參考:java多線程核心編程技術

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


分享到:


相關文章: