Java 5 中引入了新的鎖機(jī)制——java.util.concurrent.locks 中的顯式的互斥鎖:Lock 接口,
它提供了比?synchronized?更加廣泛的鎖定操作。 Lock 接口有 3 個(gè)實(shí)現(xiàn)它的類:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入鎖、讀鎖和寫鎖。 lock 必須被顯式地創(chuàng)建、鎖定和釋放,為了可以使用更多的功能,一般用 ReentrantLock 為其實(shí)例化。為了保證鎖最終一定會(huì)被釋放(可能會(huì)有異常發(fā)生),要把互斥區(qū)放在 try 語(yǔ)句塊內(nèi),并在 finally 語(yǔ)句塊中釋放鎖,尤其當(dāng)有 return 語(yǔ)句時(shí),return 語(yǔ)句必須放在 try 字句中,以確保 unlock()不會(huì)過(guò)早發(fā)生,從而將數(shù)據(jù)暴露給第二個(gè)任務(wù)。因此,采用 lock 加鎖和釋放鎖的一般形式如下:
//默認(rèn)使用非公平鎖,如果要使用公平鎖,需要傳入?yún)?shù)trueLock lock = new ReentrantLock();lock.lock(); try { // 更新對(duì)象的狀態(tài) // 捕獲異常,必要時(shí)恢復(fù)到原來(lái)的不變約束 // 如果有return語(yǔ)句,放在這里 } finally { //鎖必須在finally塊中釋放lock.unlock();}
可重入鎖,也叫做遞歸鎖,指的是同一線程外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響。 在JAVA環(huán)境下?ReentrantLock?和?synchronized?都是可重入鎖。
ReentrantReadWriteLock
讀寫鎖:分為讀鎖和寫鎖,多個(gè)讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自己控制的,你只要上好相應(yīng)的鎖即可。 如果你的代碼只讀數(shù)據(jù),可以很多人同時(shí)讀,但不能同時(shí)寫,那就上讀鎖;如果你的代碼修改數(shù)據(jù),只能有一個(gè)人在寫,且不能同時(shí)讀取,那就上寫鎖。 總之,讀的時(shí)候上讀鎖,寫的時(shí)候上寫鎖!
ReentrantReadWriteLock?會(huì)使用兩把鎖來(lái)解決問(wèn)題,一個(gè)讀鎖,一個(gè)寫鎖
線程進(jìn)入讀鎖的前提條件
沒(méi)有其他線程的寫鎖
沒(méi)有寫請(qǐng)求或者有寫請(qǐng)求,但調(diào)用線程和持有鎖的線程是同一個(gè)
線程進(jìn)入寫鎖的前提條件
沒(méi)有其他線程的讀鎖
沒(méi)有其他線程的寫鎖
StampedLock
StampedLock?是 java 8 在?java.util.concurrent.locks?新增的一個(gè)API。
ReentrantReadWriteLock?在沒(méi)有任何讀鎖和寫鎖時(shí),才可以取得寫入鎖,這可用于實(shí)現(xiàn)了悲觀讀取。 然而,如果讀取很多,寫入很少的情況下,使用?ReentrantReadWriteLock?可能會(huì)使寫入線程遭遇饑餓問(wèn)題,也就是寫入線程無(wú)法競(jìng)爭(zhēng)到鎖定而一直處于等待狀態(tài)。?StampedLock?有三種模式的鎖,用于控制讀取/寫入訪問(wèn),StampedLock 的狀態(tài)由版本和模式組成。 鎖獲取操作返回一個(gè)用于展示和訪問(wèn)鎖狀態(tài)的票據(jù)(stamp)變量,它用相應(yīng)的鎖狀態(tài)表示并控制訪問(wèn),數(shù)字0表示沒(méi)有寫鎖被授權(quán)訪問(wèn)。 在讀鎖上分為悲觀鎖和樂(lè)觀鎖,鎖釋放以及其他相關(guān)方法需要使用郵戳(stamps)變量作為參數(shù),如果他們和當(dāng)前鎖狀態(tài)不符則失敗,這三種模式為:
寫入:方法writeLock可能為了獲取獨(dú)占訪問(wèn)而阻塞當(dāng)前線程,返回一個(gè)stamp變量,能夠在unlockWrite方法中使用從而釋放鎖。也提供了tryWriteLock。 當(dāng)鎖被寫模式所占有,沒(méi)有讀或者樂(lè)觀的讀操作能夠成功。
讀?。悍椒╮eadLock可能為了獲取非獨(dú)占訪問(wèn)而阻塞當(dāng)前線程,返回一個(gè)stamp變量,能夠在unlockRead方法中用于釋放鎖。也提供了tryReadLock。
樂(lè)觀讀?。悍椒?tryOptimisticRead?返回一個(gè)非 0 郵戳變量,僅在當(dāng)前鎖沒(méi)有以寫入模式被持有。如果在獲得stamp變量之后沒(méi)有被寫模式持有,方法validate將返回true。 這種模式可以被看做一種弱版本的讀鎖,可以被一個(gè)寫入者在任何時(shí)間打斷。樂(lè)觀讀取模式僅用于短時(shí)間讀取操作時(shí)經(jīng)常能夠降低競(jìng)爭(zhēng)和提高吞吐量。
悲觀鎖(Pessimistic Lock),顧名思義,就是很悲觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)block直到它拿到鎖。 悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。 Java synchronized 就屬于悲觀鎖的一種實(shí)現(xiàn),每次線程要修改數(shù)據(jù)時(shí)都先獲得鎖,保證同一時(shí)刻只有一個(gè)線程能操作數(shù)據(jù),其他線程則會(huì)被block。
樂(lè)觀鎖(Optimistic Lock),顧名思義,就是很樂(lè)觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在提交更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù)。 樂(lè)觀鎖適用于讀多寫少的應(yīng)用場(chǎng)景,這樣可以提高吞吐量。 樂(lè)觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。