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 \。如果计算饱和,失败。(这可以>
现在我们回过头来看写锁获取方法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(电流); }