獨佔鎖與共享鎖

2021-09-26 01:25:12 字數 2664 閱讀 3524

原始碼分析

獨佔鎖和共享鎖同樣是一種概念。我們先介紹一下具體的概念,然後通過 reentrantlock 和 reentrantreadwritelock 的原始碼來介紹獨佔鎖和共享鎖。

獨佔鎖也叫排他鎖,是指該鎖一次只能被乙個執行緒所持有。如果執行緒t對資料a加上排他鎖後,則其他執行緒不能再對a加任何型別的鎖。獲得排它鎖的執行緒即能讀資料又能修改資料。jdk中的synchronized和 juc中lock的實現類就是互斥鎖。

共享鎖是指該鎖可被多個執行緒所持有。如果執行緒t對資料a加上共享鎖後,則其他執行緒只能對a再加共享鎖,不能加排它鎖。獲得共享鎖的執行緒只能讀資料,不能修改資料。 獨享鎖與共享鎖也是通過aqs來實現的,通過實現不同的方法,來實現獨享或者共享。

我們看到 reentrantreadwritelock 有兩把鎖:readlock和writelock,見名知意,乙個讀鎖乙個寫鎖, 合稱「讀寫鎖」。

再進一步觀察可以發現 readlock 和 writelock 是靠內部類 sync 實現的鎖。

sync 是 aqs 的乙個子類,這種結構在 countdownlatch 、reentrantlock 、semaphore 里面也都存在。

在reentrantreadwritelock 里面,讀鎖和寫鎖的鎖主體都是 sync ,但讀鎖和寫鎖的加鎖方式不一樣。

讀鎖是共享鎖,寫鎖是獨佔鎖。讀鎖的共享鎖可保證併發讀非常高效,而讀寫、寫讀、寫寫的過程互斥,因為讀鎖和寫鎖是分離的。所以reentrantreadwritelock的併發性相比一般的互斥鎖有了很大提公升。

在了解原始碼之前我們需要回顧一下其他知識。 在最開始提及 aqs 的時候我們也提到了state欄位(int型別,32位),該欄位用來描述有多少執行緒獲持有鎖。 在獨享鎖中這個值通常是0或者1(如果是重入鎖的話state值就是重入的次數),在共享鎖中state就是持有鎖的數量。但是在 reentrantreadwritelock 中有讀、寫兩把鎖,所以需要在乙個整型變量state上分別描述讀鎖和寫鎖的數量(或者也可以叫狀態)。於是將state變量「按位切割」切分成了兩個部分,高16位 表示讀鎖狀態(讀鎖個數),低16位表示寫鎖狀態(寫鎖個數)。如下圖所示:

)// 如果當且寫執行緒數為0,並且當前執行緒需要阻塞那麼就返回失敗;或者如果通過cas增加寫執行緒數失敗也返回失敗。

return

false

;setexclusiveownerthread

(current)

;// 如果c=0,w=0或者c>0,w>0(重入),則設定當前執行緒或鎖的擁有者

return

true

;}在取到寫鎖線程的數目後,首先判斷是否已經有執行緒持有了鎖。如果已經有執行緒持有了鎖(c!=0),則檢視當前寫鎖線程的數目,如果寫執行緒數為0(即此時存在讀鎖)或者持有鎖的執行緒不是當前執行緒就返回失敗。

如果寫入鎖的數量大於最大數(65535,2的16次方-1)就丟擲乙個error。

如果當且寫執行緒數為0(那麼讀執行緒也應該為0,因為上面已經處理c!=0的情況),並且當前執行緒需要阻塞那麼就返 回失敗;如果通過cas增加寫執行緒數失敗也返回失敗。

如果c=0,w=0或者c>0,w>0(重入),則設定當前執行緒或鎖的擁有者,返回成功!

tryacquire()除了重入條件(當前執行緒為獲取了寫鎖的執行緒)之外,增加了乙個讀鎖是否存在的判斷。

如果存在讀鎖,則寫鎖不能被獲取,原因在於:

必須確保寫鎖的操作對讀鎖可見,如果允許讀鎖在已被獲取 的情況下對寫鎖的獲取,那麼正在運行的其他讀執行緒就無法感知到當前寫執行緒的操作。

因此,只有等待其他讀執行緒都釋放了讀鎖,寫鎖才能被當前執行緒獲取,而寫鎖一旦被獲取,則其他讀寫執行緒的後續訪問均被阻塞。寫鎖的釋放與 reentrantlock 的釋放過程基本類似,每次釋放均減少寫狀態,當寫狀態為0時表示寫鎖已被釋放,然後等待的讀寫執行緒才能夠繼續訪問讀寫鎖,同時前次寫執行緒的修改對 後續的讀寫執行緒可見。 接著是讀鎖的**:

protected

final

inttryacquireshared

(int unused)

else

if(firstreader == current)

else

return1;

}return

fulltryacquireshared

(current)

;}

可以看到在 tryacquireshared(int unused) 方法中,如果其他執行緒已經獲取了寫鎖,則當前執行緒獲取讀鎖 失敗,進入等待狀態。如果當前執行緒獲取了寫鎖或者寫鎖未被獲取,則當前執行緒(執行緒安全,依靠cas保證)增加讀狀態,成功獲取讀鎖。讀鎖的每次釋放(執行緒安全的,可能有多個讀執行緒同時釋放讀鎖)均減少讀狀態,減少的值是「1<<16」。所以讀寫鎖才能實現讀讀的過程共享,而讀寫、寫讀、寫寫的過程互斥。

Java 獨佔鎖 共享鎖

獨享鎖和共享鎖在你去讀c.u.t包下的reereentrantlock和reentrantreadwritelock你就會發現,它倆乙個是獨享乙個是共享鎖。獨享鎖 該鎖每一次只能被乙個執行緒所持有。共享鎖 該鎖可被多個執行緒共有,典型的就是reentrantreadwritelock裡的讀鎖,它的讀...

Java 獨佔鎖 共享鎖

獨享鎖和共享鎖在你去讀c.u.t包下的reereentrantlock和reentrantreadwritelock你就會發現,它倆乙個是獨享乙個是共享鎖。獨享鎖 該鎖每一次只能被乙個執行緒所持有。共享鎖 該鎖可被多個執行緒共有,典型的就是reentrantreadwritelock裡的讀鎖,它的讀...

mysql 獨佔鎖 mysql的獨佔鎖和共享鎖

當有多個事務同時更新一條sql時,mysql是如何處理的呢?很顯然,使用加鎖的方式,乙個事務獲得了鎖,進行操作,其他事務排隊乙個個等著,等當前這個事務執行完釋放鎖,其他事務獲取鎖取到鎖的進行操作。這裡的鎖就是獨佔鎖,也就是x鎖。那麼如果有查詢這一行資料時,是要加鎖嗎?不是的,mysql為了提高效能,...