当前位置: 代码迷 >> 综合 >> ReentrantReadWriteLock(1)
  详细解决方案

ReentrantReadWriteLock(1)

热度:8   发布时间:2024-02-08 01:15:22.0

当读操作远远高于写操作时,这时候使用读写锁让读-读可以并发,提高性能。

这是什么意思呢?我们知道synchronized的作用标记一个方法的时候,在并发情况下,只允许一个线程去访问,其他线程在外面等待。可这很影响性能。即使这个方法是提供读取,但也只能一个线程去访问。

@Slf4j
public class TestReadWriteLock {public static void main(String[] args) throws InterruptedException {DataContainer dataContainer = new DataContainer();new Thread(() -> {dataContainer.read();}, "t1").start();new Thread(() -> {dataContainer.write();}, "t2").start();new Thread(() -> {dataContainer.write();}, "t3").start();new Thread(() -> {dataContainer.read();}, "t4").start();}
}@Slf4j
class DataContainer {private Object data;private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();private ReentrantReadWriteLock.ReadLock r = rw.readLock();private ReentrantReadWriteLock.WriteLock w = rw.writeLock();public Object read() {log.debug("准备获取读锁...");r.lock();try {log.debug("读取中");sleep(1);return data;} finally {log.debug("释放读锁...");r.unlock();}}public void write() {log.debug("准备获取写锁...");w.lock();log.debug("w入了");try {sleep(1);} finally {log.debug("释放w锁...");w.unlock();}}
}

结果如下图=

17:00:11.867 c.DataContainer [t2] - 准备获取写锁...
17:00:11.867 c.DataContainer [t1] - 准备获取读锁...
17:00:11.867 c.DataContainer [t3] - 准备获取写锁...
17:00:11.867 c.DataContainer [t4] - 准备获取读锁...
17:00:11.870 c.DataContainer [t3] - w入了
17:00:12.871 c.DataContainer [t3] - 释放w锁...
17:00:12.871 c.DataContainer [t2] - w入了
17:00:13.871 c.DataContainer [t2] - 释放w锁...
17:00:13.871 c.DataContainer [t4] - 读取中
17:00:13.871 c.DataContainer [t1] - 读取中
17:00:14.872 c.DataContainer [t4] - 释放读锁...
17:00:14.872 c.DataContainer [t1] - 释放读锁...

会发现:

读操作的线程,打印信息都是同一时刻,不需要等待。
写操作的线程,有先后顺序,也就是说,需要等待,锁在着效果。

先看读操作源码:获取锁和释放锁

  //第一步:public void lock() {this.sync.acquireShared(1);}//没锁前默认锁状态为0,且读锁和写锁标记不一样。读锁是共享,写锁是独占。//第二步:tryAcquireShared方法判断有没有给其他线程占据锁public final void acquireShared(int var1) {if (this.tryAcquireShared(var1) < 0) {this.doAcquireShared(var1);}}//第三步protected final int tryAcquireShared(int var1) {//获取当前线程信息Thread var2 = Thread.currentThread();//获取当前锁状态int var3 = this.getState();//如果当前状态不等于0,给写锁占据,且占据锁的不是当前线程,返回-1表示失败。//if (exclusiveCount(var3) != 0 && this.getExclusiveOwnerThread() != var2) {return -1;} else {//如果是占据锁的是当前线程或者锁的状态为零,且锁重入的次数小于65535,则cas改变锁重入的次数。返回1,表示成功。后面的线程由于读锁发生共享,共享次数发生改变,每次自增1.int var4 = sharedCount(var3);if (!this.readerShouldBlock() && var4 < 65535 && this.compareAndSetState(var3, var3 + 65536)) {if (var4 == 0) {this.firstReader = var2;this.firstReaderHoldCount = 1;} else if (this.firstReader == var2) {++this.firstReaderHoldCount;} else {ReentrantReadWriteLock.Sync.HoldCounter var5 = this.cachedHoldCounter;if (var5 != null && var5.tid == ReentrantReadWriteLock.getThreadId(var2)) {if (var5.count == 0) {this.readHolds.set(var5);}} else {this.cachedHoldCounter = var5 = (ReentrantReadWriteLock.Sync.HoldCounter)this.readHolds.get();}++var5.count;}return 1;} else {return this.fullTryAcquireShared(var2);}}}

读锁释放锁:

 //第一步:因为锁成功会加1,锁重入次数也会加1.所以释放的时候,要每次减1.public final boolean releaseShared(int var1) {if (this.tryReleaseShared(var1)) {this.doReleaseShared();return true;} else {return false;}}
 //对当前线程锁状态进行减操作,判断锁的重入次数是否全部释放完成。protected final boolean tryReleaseShared(int var1) {Thread var2 = Thread.currentThread();int var4;if (this.firstReader == var2) {if (this.firstReaderHoldCount == 1) {this.firstReader = null;} else {--this.firstReaderHoldCount;}} else {ReentrantReadWriteLock.Sync.HoldCounter var3 = this.cachedHoldCounter;if (var3 == null || var3.tid != ReentrantReadWriteLock.getThreadId(var2)) {var3 = (ReentrantReadWriteLock.Sync.HoldCounter)this.readHolds.get();}var4 = var3.count;if (var4 <= 1) {this.readHolds.remove();if (var4 <= 0) {throw this.unmatchedUnlockException();}}--var3.count;}int var5;do {var5 = this.getState();var4 = var5 - 65536;} while(!this.compareAndSetState(var5, var4));return var4 == 0;}
 //锁状态减为0之后,判断头结点是否为空,如果不为空,且结点的等待状态为-1,则需uppark下一个结点,改变节点的状态。如果没有下一个结点,头结点保持为空private void doReleaseShared() {while(true) {AbstractQueuedSynchronizer.Node var1 = this.head;if (var1 != null && var1 != this.tail) {int var2 = var1.waitStatus;if (var2 == -1) {if (!compareAndSetWaitStatus(var1, -1, 0)) {continue;}this.unparkSuccessor(var1);} else if (var2 == 0 && !compareAndSetWaitStatus(var1, 0, -3)) {continue;}}if (var1 == this.head) {return;}}}