java并发编程 ReentrantReadWriteLock详解


文章目录

1 ReentrantReadWriteLock是什么?2 相关文章3 示例2 ReentrantReadWriteLock结构3 写锁 WriteLock实现原理3.1 WriteLock数据结构 4 读锁 ReadLock实现原理4.1 ReadLock数据结构 5 ReentrantReadWriteLock.Sync实现原理5.1 Sync数据结构5.2 ReadLock详解5.2.1 lock()5.2.2 unlock()5.2.3 其他5.2.4 小总结 5.3 WriteLock详解 6 总结

1 ReentrantReadWriteLock是什么?

按照类注释上的解释:ReentrantReadWriteLock是读写锁的实现,且支持ReentrantLock类似的语义即可重入。
读写锁定义:(读锁)共享锁和(写锁)排他锁,即读锁和读锁之间不互斥,写锁和谁都互斥,也就是可以共享读。因为读是不涉及修改的,不会对一致性造成影响。
所以ReentrantReadWriteLock提供了可重入机制的读锁和写锁,相对于ReentrantLock多了读锁,减小了锁的粒度。

2 相关文章

java 并发编程系列文章目录

3 示例

读锁和写锁的使用示例,即读数据的时候使用读锁,这样可以给别人读的线程留一条路,要对数据变动的时候使用写锁,保证只有一个线程修改。获取锁之后再次判断(基本使用判断逻辑)。
这是ReentrantReadWriteLock类上面注释的例子

class CachedData {Object data;volatile boolean cacheValid;final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();void processCachedData() {rwl.readLock().lock();if (!cacheValid) {// Must release read lock before acquiring write lock        rwl.readLock().unlock();rwl.writeLock().lock();        try {          // Recheck state because another thread might have          // acquired write lock and changed state before we did.          if (!cacheValid) {            data = ...            cacheValid = true;          }          // Downgrade by acquiring read lock before releasing write lock          rwl.readLock().lock();        } finally {          rwl.writeLock().unlock(); // Unlock write, still hold read        }      }        try {        use(data);      } finally {        rwl.readLock().unlock();     }   }  }

2 ReentrantReadWriteLock结构

可以理解为当前这把大锁细分成读锁和写锁,读锁和写锁持有sync(aqs),实现加锁和释放锁逻辑。

//读锁private final ReentrantReadWriteLock.ReadLock readerLock;//写锁private final ReentrantReadWriteLock.WriteLock writerLock;//同步器 实现了AQS的final Sync sync;public ReentrantReadWriteLock() {this(false);}//默认非公平锁,创建读锁写锁对象传入this, 读写锁使用的是同一个syncpublic ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}//...忽略 WriteLock 数据结构 ReadLock数据结构,下面会涉及到

3 写锁 WriteLock实现原理

3.1 WriteLock数据结构

从结构上看,是不是就是通过Sync 对其加锁释放锁,先把sync当成黑盒(下面会描述),也即通过aqs加锁和释放锁,如果对AQS不了解,请一定看相关文章

public static class WriteLock implements Lock, java.io.Serializable {private static final long serialVersionUID = -4992448646407690164L;private final Sync sync;protected WriteLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquire(1);}public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}public boolean tryLock( ) {return sync.tryWriteLock();}public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}public void unlock() {sync.release(1);}public Condition newCondition() {return sync.newCondition();}public boolean isHeldByCurrentThread() {return sync.isHeldExclusively();}}

4 读锁 ReadLock实现原理

4.1 ReadLock数据结构

可以看到 读锁使用的是sync的共享锁的获取和释放逻辑,即AQS的Shared的逻辑。所以重点看sync对于AQS的实现。

    public static class ReadLock implements Lock, java.io.Serializable {private static final long serialVersionUID = -5992448646407690164L;private final Sync sync;protected ReadLock(ReentrantReadWriteLock lock) {sync = lock.sync;}public void lock() {sync.acquireShared(1);}public void lockInterruptibly() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public boolean tryLock() {return sync.tryReadLock();}public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}public void unlock() {sync.releaseShared(1);}//读锁也不用等待。。。所以抛异常public Condition newCondition() {throw new UnsupportedOperationException();}}

5 ReentrantReadWriteLock.Sync实现原理

5.1 Sync数据结构

在ReentrantLock里,是通过int state的从0原子设置成1代表获取成功,然后重入 1,释放-1。如果不了解请查看java并发编程 ReentrantLock详解。因为ReentrantLock就是独占锁,所以使用一个state即可,但是此时一个sync需要满足两种锁,所以把一个int值掰成两份用,高16位共享,低16位独占。

        static final int SHARED_SHIFT   = 16;static final int SHARED_UNIT    = (1