深入理解读写锁ReentrantReadWriteLock

  


https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q


1。读写锁的介绍

1。读写锁的介绍

在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java提供的关键字同步或者并发包中实现了锁接口的。它们都是独占式获取锁,也就是在同一时刻只有一个线程能够获取锁。而在一些业务场景中,大部分只是读数据,写数据很少,如果仅仅是读数据的话并不会影响数据正确性(出现脏读),而如果在这种业务场景下,依然使用独占锁的话,很显然这将是出现性能瓶颈的地方。

针对这种读多写少的情况,java还提供了另外一个实现锁接口的ReentrantReadWriteLock(读写锁),在分析WirteLock和ReadLock的互斥性时可以按照WriteLock与WriteLock之间,WriteLock与ReadLock之间以及ReadLock与ReadLock之间进行分析。更多关于读写锁特性介绍大家可以看源码上的介绍(阅读源码时最好的一种学习方式,我也正在学习中,与大家共勉),这里做一个归纳总结:

<李>

:支持非公平性(默认)和公平的锁获取方式,吞吐量还是非公平优于公平;

<李>

:支持重入,读锁获取后能再次获取,写锁获取之后能够再次获取写锁,同时也能够获取读锁;

<李>

:遵循获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁

要想能够彻底的理解读写锁必须能够理解这样几个问题:1。读写锁是怎样实现分别记录读写状态的? 2。写锁是怎样获取和释放的? 3。读锁是怎样获取和释放的?我们带着这样的三个问题,再去了解下读写锁。

2。写锁详解

2.1。写锁的获取

同步组件的实现聚合了同步器(aq),并通过重写重写同步器(aq)中的方法实现同步组件的同步语义,aq的底层实现分析可以,因此,写锁的实现依然也是采用这种方式。在同一时刻写锁是不能被多个线程所获取,很显然写锁是独占式锁,而实现写锁的同步语义是通过重写aq中的tryAcquire方法实现的。源码为:

<>之前保护最终布尔tryAcquire (int获得){/*   *介绍:   * 1 \。如果读计数非零或写数非零   *和所有者是一个不同的线程,失败。   * 2 \。如果计算饱和,失败。(这可以>

深入理解读写锁ReentrantReadWriteLock

现在我们回过头来看写锁获取方法tryAcquire,其主要逻辑为:

2.2。写锁的释放

写锁释放通过重写aq的tryRelease方法,源码为:

<>之前保护最终布尔tryRelease (int版本){   如果(! isHeldExclusively ())   把新IllegalMonitorStateException ();//1 \。同步状态减去写状态   int nextc=getState()——发布;//2 \。当前写状态是否为0,为0则释放写锁   布尔自由=exclusiveCount (nextc)==0;   如果(免费)   setExclusiveOwnerThread(空);//3 \。不为0则更新同步状态   设置状态(nextc);   返回自由;   }

源码的实现逻辑请看注释,不难理解与ReentrantLock基本一致,这里需要注意的是,减少写状态<代码> int nextc=getState()——发布;>

3。读锁详解

3.1。读锁的获取

看完了写锁,现在来看看读锁,读锁不是独占式锁,即同一时刻该锁可以被多个读线程获取也就是一种共享式锁。按照之前对aq介绍,实现共享式同步组件的同步语义需要通过重写aq的tryAcquireShared方法和tryReleaseShared方法。读锁的获取实现方法为:

<>之前保护最终int tryAcquireShared (int未使用){/*   *介绍:   * 1 \。如果另一个线程持有的锁,写失败。   * 2 \。否则,这个线程是合格的   *关于锁状态,所以问应该阻止   *由于队列政策。如果不是,试一试   *由国家授予套管和更新计数。   *注意,一步不检查可重入   *获得,推迟到完整版   *为了避免检查持有数   *更典型的不可重入的。   * 3 \。如果步骤2失败不是因为线程   *显然不合格或CAS失败或计数   *饱和链与完整版重试循环。   */当前线程=Thread.currentThread ();   int c=getState ();//1 \。如果写锁已经被获取并且获取写锁的线程不是当前线程的话,当前//线程获取读锁失败返回1   如果(exclusiveCount (c) !=0,,   getExclusiveOwnerThread() !=当前)   返回1;   int r=sharedCount (c);   如果(! readerShouldBlock (),,   r & lt;MAX_COUNT,,//2 \。当前线程获取读锁   compareAndSetState (c, c + SHARED_UNIT)) {//3 \。下面的代码主要是新增的一些功能,比如getReadHoldCount()方法//返回当前获取读锁的次数   如果(r==0) {   firstReader=当前;   firstReaderHoldCount=1;   }else if (firstReader==当前){   firstReaderHoldCount + +;   其他}{   HoldCounter rh=cachedHoldCounter;   如果(rh==null | | rh。tid !=getThreadId(当前)   cachedHoldCounter=rh=readHolds.get ();   else if (rh。数==0)   readHolds.set (rh);   rh.count + +;   }   返回1;   }//4 \。处理在第二步中CAS操作失败的自旋已经实现重入性   返回fullTryAcquireShared(电流);   }

深入理解读写锁ReentrantReadWriteLock