StampedLock類是JDK8里面新增的一個(gè)并發(fā)工具類,,這個(gè)類比較特殊,在此之前我們先簡單的了解一下關(guān)于數(shù)據(jù)庫或者存儲(chǔ)系統(tǒng)的鎖策略和機(jī)制,。 總體上來說鎖有兩大類: 悲觀鎖:總是認(rèn)為會(huì)有沖突發(fā)生,,所以每次操作臨界區(qū)資源時(shí)都會(huì)加鎖。 樂觀鎖:顧名思義,認(rèn)為每次操作臨界區(qū)資源時(shí)不會(huì)發(fā)生沖突,,但會(huì)先記錄一個(gè)版本號(hào),,在提交事務(wù)時(shí),會(huì)檢查版本號(hào)是否變更,,從而作出判斷放棄或者重試,。 對于一個(gè)高并發(fā)的應(yīng)用程序來說,數(shù)據(jù)庫常常會(huì)成為一個(gè)訪問的瓶頸,,這里面主要存在以下的幾種訪問情況: (1)讀讀并發(fā) (2)讀寫并發(fā) (3)寫寫并發(fā) 一般情況下,,數(shù)據(jù)庫都會(huì)有讀共享寫?yīng)氄嫉逆i并發(fā)的方案,也就是說讀讀并發(fā)是沒問題的,,但在讀寫并發(fā)時(shí),,則有可能出現(xiàn)讀取不一致情況,也就是常說的臟讀,,所以在悲觀鎖的模式下,,在有寫線程的時(shí)候,是不允許有任何其他的讀和寫線程的,,也就是說寫是獨(dú)占的,,這樣會(huì)導(dǎo)致系統(tǒng)的吞吐明顯下降,如何避免這一情況,,于是就出現(xiàn)了基于MVCC多版本控制并發(fā)的策略,,在這種策略下讀寫并發(fā)是可以同時(shí)進(jìn)行的,底層的原理是當(dāng)前有并發(fā)的寫線程在獨(dú)占,,那么讀線程就直接讀取事務(wù)log里面的歷史最新版本的數(shù)據(jù),,這樣以來就大大提高了并發(fā)吞吐能力,雖然讀取的數(shù)據(jù)并不是最新的數(shù)據(jù),,但是歷史上最新的,,同時(shí)也保持了一致性,目前主流的數(shù)據(jù)庫都支持這種模式,。最后一種是寫寫并發(fā)場景,,這種場景通常基于樂觀鎖的并發(fā)寫方案也稱OCC,,多個(gè)并發(fā)的寫線程,,每個(gè)線程都不會(huì)修改原始數(shù)據(jù),而是從原始數(shù)據(jù)上拷貝上一份數(shù)據(jù),,同時(shí)記錄版本號(hào),,不同的線程更新自己的數(shù)據(jù),在最終寫會(huì)時(shí)會(huì)判斷版本號(hào)是否變更,,如果變更則意味有人已經(jīng)更改過了,,那么當(dāng)前線程需要做的就是自旋重試,,如果重試指定的次數(shù)依然失敗,那么就應(yīng)該放棄更新,,這種策略僅僅適合寫并發(fā)并不強(qiáng)烈的場景,,如果寫競爭嚴(yán)重,那么多次自旋重試的開銷也是非常耗性能的,,如果競爭激烈,,那么寫鎖獨(dú)占的方式則更加適合。 基于上面談到的這些內(nèi)容,,我們再來分析StampedLock類,,就會(huì)非常比較容易理解,它實(shí)際主要解決的是讀寫并發(fā)場景更加類似于上面我們談到的MVCC的模式,。 StampedLock類有三種模式: (一)寫鎖,,這里的寫鎖是獨(dú)占和排它的,,這里對于申請寫鎖成功的線程會(huì)得到一個(gè)stamp,,在釋放鎖unlockWrite(long)的時(shí)候會(huì)傳入這個(gè)票據(jù),申請寫鎖還支持非阻塞模式的調(diào)用通過tryWriteLock方法或者可超時(shí)的申請,,處于寫鎖狀態(tài)下,,任何其他的寫鎖,讀鎖,,樂觀讀鎖都會(huì)失敗,。 (二)讀鎖,申請成功會(huì)返回一個(gè)票據(jù),,同理在釋放的時(shí)候unlockRead(long)也需要傳回票據(jù),。讀鎖是共享的,前提是沒有任何寫鎖占用,。 (三)樂觀讀鎖,,是新的特性,這種策略非常輕量級(jí),,在操作數(shù)據(jù)時(shí)候并沒有使用CAS來設(shè)置鎖的狀態(tài),,如果當(dāng)前沒有線程持有寫鎖,那么樂觀讀鎖就會(huì)立即返回一個(gè)非0的票據(jù),,在獲得之后,,為了保持一致性,要拷貝需要使用的相關(guān)數(shù)據(jù)到線程的的棧里面,,然后再次判斷票據(jù)是否有效,,如果無效,則意味著這期間有線程修改了數(shù)據(jù)狀態(tài),,所以這時(shí)候要么放棄操作,,要么直接申請讀鎖,如果票據(jù)有效則意味著,當(dāng)前的數(shù)據(jù)沒有被更改過,,可能不是最新的,,但是一致的,在讀寫并發(fā)時(shí)候,,用來讀取是沒有問題的,,所以效率會(huì)高很多,因?yàn)闆]有使用任何的加鎖操作,,我們可以理解讀取的數(shù)據(jù)類似MVCC的快照,,最多不是最新的,但一定是一致的,。 StampedLock類的主要特點(diǎn),,我認(rèn)為有兩個(gè): (1)通過樂觀讀鎖支持讀寫并發(fā),這里使用的是票據(jù)對比,。 (2)支持讀鎖升級(jí)成寫鎖 下面我們看一下官網(wǎng)給出的例子,,分別展示了寫鎖,樂觀讀鎖和讀鎖升級(jí)成寫鎖的案例:
總結(jié): 本文主要介紹了JDK8里面新增的并發(fā)工具類StampedLock,,相比ReentrantLock重入鎖提供了更好的性能,并且支持讀寫并發(fā)的場景和讀鎖升級(jí)成寫鎖的功能,,在使用時(shí)候一定注意樂觀讀鎖需要先獲取票據(jù),,然后在拷貝實(shí)例數(shù)據(jù)到線程棧,然后接著判斷票據(jù)是否有效,,如果位置搞反,,那么則有可能使用出錯(cuò),這一點(diǎn)需要注意,。最后我們還要記住StampedLock是不支持重入的,,盡管你可以通過鎖轉(zhuǎn)換來變相實(shí)現(xiàn),還有對于StampedLock鎖這里并沒有明確強(qiáng)調(diào)公平和非公平的概念,,這里StampedLock會(huì)盡量保證最好的性能,。 https://docs.oracle.com/javase/8/docs/api/ https://www.jianshu.com/p/481071ddafd3 https://netjs./2016/08/stampedlock-in-java.html |
|