日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock

發(fā)布時(shí)間:2024/9/30 java 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/zhaoyanjun6/article/details/120750932
本文出自【趙彥軍的博客】

Java線程安全StampedLock
Java線程安全Lock、ReentrantLock、ReentrantReadWriteLock
Java線程安全集合總結(jié)
Java原子操作Atomic

文章目錄

  • 前言
  • Lock
  • ReentrantLock
    • 公平鎖/非公平鎖
    • 超時(shí)機(jī)制
    • 可重入鎖
  • 讀寫鎖 ReentrantReadWriteLock
    • 源碼結(jié)構(gòu)
    • 總結(jié)
    • 示例

前言

java5之后,并發(fā)包中新增了Lock接口(以及相關(guān)實(shí)現(xiàn)類)用來(lái)實(shí)現(xiàn)鎖的功能,它提供了與synchronized關(guān)鍵字類似的同步功能。

既然有了synchronized這種內(nèi)置的鎖功能,為何要新增Lock接口?先來(lái)想象一個(gè)場(chǎng)景:手把手的進(jìn)行鎖獲取和釋放,先獲得鎖A,然后再獲取鎖B,當(dāng)獲取鎖B后釋放鎖A同時(shí)獲取鎖C,當(dāng)鎖C獲取后,再釋放鎖B同時(shí)獲取鎖D,以此類推,這種場(chǎng)景下,synchronized關(guān)鍵字就不那么容易實(shí)現(xiàn)了,而使用Lock卻顯得容易許多。

Lock

Lock 是一個(gè)接口

public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition(); }

ReentrantLock

public class ReentrantLock implements Lock, java.io.Serializable { }

使用如下:

public class LockTest {Lock lock = new ReentrantLock();public void run(){//獲取鎖 lock.lock();//doSomething//釋放鎖lock.unlock();} }

使用起來(lái),也非常簡(jiǎn)單,首先 lock.lock(); 獲取鎖,然后去執(zhí)行自己的邏輯,最后調(diào)用 lock.unlock(); 釋放鎖。

但是這里有個(gè)問題,如果我們的邏輯發(fā)生異常,那就永遠(yuǎn)無(wú)法釋放鎖,所以我們優(yōu)化一下,把釋放鎖的邏輯放在 finally 塊中,如下

public class LockTest {Lock lock = new ReentrantLock();public void run(){//獲取鎖lock.lock();try {//doSomething}catch (Exception e){}finally {//釋放鎖lock.unlock();}} }

公平鎖/非公平鎖

  • 非公平鎖:如果同時(shí)還有另一個(gè)線程進(jìn)來(lái)嘗試獲取,那么有可能會(huì)讓這個(gè)線程搶先獲取;

  • 公平鎖:如果同時(shí)還有另一個(gè)線程進(jìn)來(lái)嘗試獲取,當(dāng)它發(fā)現(xiàn)自己不是在隊(duì)首的話,就會(huì)排到隊(duì)尾,由隊(duì)首的線程獲取到鎖。

ReentrantLock 主要利用CAS+AQS隊(duì)列來(lái)實(shí)現(xiàn)。它支持公平鎖和非公平鎖,兩者的實(shí)現(xiàn)類似。

  • CAS:Compare and Swap,比較并交換。CAS有3個(gè)操作數(shù):內(nèi)存值V、預(yù)期值A(chǔ)、要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。該操作是一個(gè)原子操作,被廣泛的應(yīng)用在Java的底層實(shí)現(xiàn)中。在Java中,CAS主要是由sun.misc.Unsafe這個(gè)類通過(guò)JNI調(diào)用CPU底層指令實(shí)現(xiàn)

ReentrantLock主要利用CAS+AQS隊(duì)列來(lái)實(shí)現(xiàn)。它支持公平鎖和非公平鎖,兩者的實(shí)現(xiàn)類似。

AbstractQueuedSynchronizer 簡(jiǎn)稱AQS

我們來(lái)看一下 ReentrantLock 構(gòu)造函數(shù)

public ReentrantLock() {sync = new NonfairSync();}/*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

構(gòu)造支持傳入 fair 來(lái)指定是否是公平鎖

  • FairSync() 公平鎖
  • NonfairSync() 非公平鎖

如果使用無(wú)參構(gòu)造函數(shù),則是非公平鎖。

超時(shí)機(jī)制

在 ReetrantLock的tryLock(long timeout, TimeUnit unit) 提供了超時(shí)獲取鎖的功能。它的語(yǔ)義是在指定的時(shí)間內(nèi)如果獲取到鎖就返回true,獲取不到則返回false。這種機(jī)制避免了線程無(wú)限期的等待鎖釋放。

可重入鎖

  • 可重入鎖。可重入鎖是指同一個(gè)線程可以多次獲取同一把鎖。ReentrantLock和synchronized都是可重入鎖。

  • 可中斷鎖。可中斷鎖是指線程嘗試獲取鎖的過(guò)程中,是否可以響應(yīng)中斷。synchronized是不可中斷鎖,而ReentrantLock則提供了中斷功能。

  • 公平鎖與非公平鎖。公平鎖是指多個(gè)線程同時(shí)嘗試獲取同一把鎖時(shí),獲取鎖的順序按照線程達(dá)到的順序,而非公平鎖則允許線程“插隊(duì)”。synchronized是非公平鎖,而ReentrantLock的默認(rèn)實(shí)現(xiàn)是非公平鎖,但是也可以設(shè)置為公平鎖。

讀寫鎖 ReentrantReadWriteLock

現(xiàn)實(shí)中有這樣一種場(chǎng)景:對(duì)共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁。

在沒有寫操作的時(shí)候,多個(gè)線程同時(shí)讀一個(gè)資源沒有任何問題,所以應(yīng)該允許多個(gè)線程同時(shí)讀取共享資源;但是如果一個(gè)線程想去寫這些共享資源,就不應(yīng)該允許其他線程對(duì)該資源進(jìn)行讀和寫的操作了。

針對(duì)這種場(chǎng)景,JAVA的并發(fā)包提供了讀寫鎖 ReentrantReadWriteLock,它表示兩個(gè)鎖,一個(gè)是讀操作相關(guān)的鎖,稱為共享鎖;一個(gè)是寫相關(guān)的鎖,稱為排他鎖,描述如下:

線程進(jìn)入讀鎖的前提條件:

  • 沒有其他線程的寫鎖,

  • 沒有寫請(qǐng)求或者有寫請(qǐng)求,但調(diào)用線程和持有鎖的線程是同一個(gè)。

線程進(jìn)入寫鎖的前提條件:

  • 沒有其他線程的讀鎖

  • 沒有其他線程的寫鎖

而讀寫鎖有以下三個(gè)重要的特性:

(1)公平選擇性:支持非公平(默認(rèn))和公平的鎖獲取方式,吞吐量還是非公平優(yōu)于公平。

(2)重進(jìn)入:讀鎖和寫鎖都支持線程重進(jìn)入。

(3)鎖降級(jí):遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級(jí)成為讀鎖。

源碼結(jié)構(gòu)

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {/** 讀鎖 */private final ReentrantReadWriteLock.ReadLock readerLock;/** 寫鎖 */private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;/** 使用默認(rèn)(非公平)的排序?qū)傩詣?chuàng)建一個(gè)新的 ReentrantReadWriteLock */public ReentrantReadWriteLock() {this(false);}/** 使用給定的公平策略創(chuàng)建一個(gè)新的 ReentrantReadWriteLock */public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}/** 返回用于寫入操作的鎖 */public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }/** 返回用于讀取操作的鎖 */public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {}static final class NonfairSync extends Sync {}static final class FairSync extends Sync {}public static class ReadLock implements Lock, java.io.Serializable {}public static class WriteLock implements Lock, java.io.Serializable {} }

1、類的繼承關(guān)系

public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable {}

說(shuō)明:可以看到,ReentrantReadWriteLock實(shí)現(xiàn)了ReadWriteLock接口,ReadWriteLock接口定義了獲取讀鎖和寫鎖的規(guī)范,具體需要實(shí)現(xiàn)類去實(shí)現(xiàn);同時(shí)其還實(shí)現(xiàn)了Serializable接口,表示可以進(jìn)行序列化,在源代碼中可以看到ReentrantReadWriteLock實(shí)現(xiàn)了自己的序列化邏輯。

2、ReentrantReadWriteLock有五個(gè)內(nèi)部類,五個(gè)內(nèi)部類之間也是相互關(guān)聯(lián)的。內(nèi)部類的關(guān)系如下圖所示。


說(shuō)明:如上圖所示,Sync繼承自AQS、NonfairSync繼承自Sync類、FairSync繼承自Sync類(通過(guò)構(gòu)造函數(shù)傳入的布爾值決定要構(gòu)造哪一種Sync實(shí)例);ReadLock實(shí)現(xiàn)了Lock接口、WriteLock也實(shí)現(xiàn)了Lock接口。

總結(jié)

  • 在線程持有讀鎖的情況下,該線程不能取得寫鎖(因?yàn)楂@取寫鎖的時(shí)候,如果發(fā)現(xiàn)當(dāng)前的讀鎖被占用,就馬上獲取失敗,不管讀鎖是不是被當(dāng)前線程持有)。

  • 在線程持有寫鎖的情況下,該線程可以繼續(xù)獲取讀鎖(獲取讀鎖時(shí)如果發(fā)現(xiàn)寫鎖被占用,只有寫鎖沒有被當(dāng)前線程占用的情況才會(huì)獲取失敗)。

仔細(xì)想想,這個(gè)設(shè)計(jì)是合理的:因?yàn)楫?dāng)線程獲取讀鎖的時(shí)候,可能有其他線程同時(shí)也在持有讀鎖,因此不能把獲取讀鎖的線程“升級(jí)”為寫鎖;而對(duì)于獲得寫鎖的線程,它一定獨(dú)占了讀寫鎖,因此可以繼續(xù)讓它獲取讀鎖,當(dāng)它同時(shí)獲取了寫鎖和讀鎖后,還可以先釋放寫鎖繼續(xù)持有讀鎖,這樣一個(gè)寫鎖就“降級(jí)”為了讀鎖。

綜上:

一個(gè)線程要想同時(shí)持有寫鎖和讀鎖,必須先獲取寫鎖再獲取讀鎖;寫鎖可以“降級(jí)”為讀鎖;讀鎖不能“升級(jí)”為寫鎖。

示例

public class LockTest {ReadWriteLock lock = new ReentrantReadWriteLock();Lock readLock = lock.readLock();Lock writeLock = lock.writeLock();}

總結(jié)

以上是生活随笔為你收集整理的Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。