1、synchronized關(guān)鍵字的機(jī)制是對象鎖(把這個對象鎖住【這個對象的帶synchronized關(guān)鍵字的方法鎖住】)
如? ?class A
{
? ? public?synchronized void a()
? ? {
????}
}
在方法a()上加上synchronized關(guān)鍵字,在多個線程中無法同時調(diào)用A類的某個對象的a()方法。
關(guān)鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法(函數(shù))當(dāng)作鎖,哪個線程先執(zhí)行帶synchronized關(guān)鍵字的方法,哪個線程就持有該方法所屬對象的鎖,其他線程都只能呈等待狀態(tài)。但是這有個前提:既然鎖叫做對象鎖,那么勢必和對象相關(guān),所以多個線程訪問的必須是同一個對象。
如果多個線程訪問的是多個對象,那么Java虛擬機(jī)就會創(chuàng)建多個鎖,既然兩個線程持有的是不同的鎖,自然不會受到"等待釋放鎖"這一行為的制約,可以分別運(yùn)行方法中的代碼。
在一個類中的兩個方法一個加了synchronized,一個未加synchronized,兩個線程是可以同時分別執(zhí)行兩個方法的,但是兩個方法都加synchronized,則必須順序執(zhí)行
兩個結(jié)論:
1、A線程持有Object對象的Lock鎖,B線程可以以異步方式調(diào)用Object對象中的非synchronized類型的方法
2、A線程持有Object對象的Lock鎖,B線程如果在這時調(diào)用Object對象中的synchronized類型的方法則需要等待,也就是同步
換言之,一個線程調(diào)用一個帶了synchronized關(guān)鍵字的方法,則這個線程就獲得了該對象的鎖,其他線程想執(zhí)行該對象的的所有帶synchronized的方法,都得等這個線程執(zhí)行完才可以去競爭。但是可以直接執(zhí)行非同步的方法。
2、synchronized鎖重入
關(guān)鍵字synchronized擁有鎖重入的功能。所謂鎖重入的意思就是:當(dāng)一個線程得到一個對象鎖后,再次請求此對象鎖時時可以再次得到該對象的鎖的。
在一個有synchronized關(guān)鍵的方法中調(diào)用另一個有synchronized關(guān)鍵的方法,該線程將重新獲取該對象的鎖
3、異常自動釋放鎖
當(dāng)一個線程執(zhí)行的代碼出現(xiàn)異常時,其所持有的鎖會自動釋放。
4、synchronized同步代碼塊
當(dāng)執(zhí)行一個同步方法時間較長的時候,其他等待線程將會長時間等待,這個情況可以將方法中需要同步的部分設(shè)置為synchronized同步代碼塊,這部分代碼就只有一個縣城能同時執(zhí)行,而其余代碼可以多個線程同時執(zhí)行。
synchronized(this)
? ? ? ? {
。。。。。。。。。。。。。。
? ? ? ? }
結(jié)論:
1、當(dāng)A線程訪問對象的synchronized代碼塊的時候,B線程依然可以訪問對象方法中其余非synchronized塊的部分
2、當(dāng)A線程進(jìn)入對象的synchronized代碼塊的時候,B線程如果要訪問這段synchronized塊,那么訪問將會被阻塞
5、兩個synchronized塊之間具有互斥性
這個與1比較相似,同一個對象的兩個方法中都有synchronized塊,則兩個線程不能同時分別執(zhí)行這兩個帶有synchronized塊的方法,必須順序執(zhí)行。
結(jié)論:synchronized塊獲得的是一個對象鎖,換句話說,synchronized塊鎖定的是整個對象。
6、synchronized塊和synchronized方法
這兩個也是互斥的,執(zhí)行一個,另一個不能執(zhí)行,必須順序執(zhí)行,總之帶synchronized關(guān)鍵字就是鎖住對象。
總結(jié)一下前面的內(nèi)容:
1、synchronized同步方法
(1)對其他synchronized同步方法或synchronized(this)同步代碼塊呈阻塞狀態(tài)
(2)同一時間只有一個線程可以執(zhí)行synchronized同步方法中的代碼
2、synchronized同步代碼塊
(1)對其他synchronized同步方法或synchronized(this)同步代碼塊呈阻塞狀態(tài)
(2)同一時間只有一個線程可以執(zhí)行synchronized(this)同步代碼塊中的代碼
7、將任意對象作為對象監(jiān)視器
Java還支持對"任意對象"作為對象監(jiān)視器來實現(xiàn)同步的功能。這個"任意對象"大多數(shù)是實例變量及方法的參數(shù),使用格式為synchronized(非this對象)
多個線程持有"對象監(jiān)視器"為同一個對象的前提下,同一時間只能有一個線程可以執(zhí)行synchronized(非this對象x)代碼塊中的代碼。(在一個類中,每個synchronized(非this對象x)中的x指示的是同一個變量或參數(shù)的時候,它的作用與synchronized(this)相同)
(1)當(dāng)一個對象的兩個方法的synchronized(x)中的x不同時,他們是不互斥的,兩個線程可以同時調(diào)用這兩個方法(synchronized(this)與同步方法效果一致,一個同步方法和一個x不為this的方法塊也可以同時執(zhí)行)。
(2)當(dāng)多個線程同時執(zhí)行synchronized(x){}同步代碼塊時呈同步效果(只有一個線程能執(zhí)行)
(3)當(dāng)其他線程執(zhí)行x對象中的synchronized同步方法時呈同步效果(比如:有一個同步代碼塊synchronized(A),而A對象中存在是同步方法(同步代碼塊,必須是synchronized(this),必須是this,這樣才能表示這個對象被鎖定),并且有其他線程正在執(zhí)行此方法,也就是A對象被鎖定,這時synchronized(A)這個代碼塊也無法執(zhí)行)
(4)當(dāng)其他線程執(zhí)行x對象方法中的synchronized(this)代碼塊時也呈同步效果
其他線程對一個對象的被鎖(X)鎖住(其他線程正在執(zhí)行這段代碼,這個對象的這段代碼就被鎖住了)的代碼是無法執(zhí)行的。
8、同步靜態(tài)方法
擁有 static 與?synchronized兩個關(guān)鍵字的方法
1、如果線程A調(diào)用了某個類的靜態(tài)同步方法(直接通過類名調(diào)用或通過對象調(diào)用),則該類被上鎖(類鎖,和對象鎖不同),則該類中的靜態(tài)同步方法都無法被調(diào)用(直接通過類名調(diào)用或通過對象都無法調(diào)用),但是非靜態(tài)同步方法可以被調(diào)用。
例子:假如一個類中有一個靜態(tài)同步方法A,new出了兩個類的實例B和實例C,線程D持有實例B,線程E持有實例C,只要線程D調(diào)用了A方法,那么線程E調(diào)用A方法必須等待線程D執(zhí)行完A方法,盡管兩個線程持有的是不同的對象。
9、volatile關(guān)鍵字
java有一塊主內(nèi)存,不同線程有不同的工作內(nèi)存,所有變量在主內(nèi)存中有一份,當(dāng)線程需要用到時就去主內(nèi)存中取,用完后,把新值返還給主內(nèi)存。
在啟動一個線程A后,這個A線程將取走它需要的變量a,如果這個變量a被其他的線程B的更改,線程A是無法感知的,也就是線程A每次使用這個變量a時不會再從主內(nèi)存中獲取,就無法保證它取到的是最新的值。
解決辦法:將這個變量前加上volatile關(guān)鍵字。
10、原子類也無法保證線程安全
線程安全不是絕對的,在有邏輯的情況下輸出結(jié)果也具有隨機(jī)性
結(jié)果安全,順序會混亂。