其他分享
首页 > 其他分享> > JUC之ReadWriteLock

JUC之ReadWriteLock

作者:互联网

JUC之读写锁解读

文章目录

一、什么是读写锁

​ 读写锁实现的功能就是“读写分离”,读可以并发读,写只能串行写,同时,读的时候不能写,写的时候不能读。但是,如何控制读与写,需要我们手动在读代码块上加读锁,写代码上加写锁。

二、读写锁的实现

​ 这里我主要讲一些内部实现原理。

final boolean tryReadLock() {
    Thread current = Thread.currentThread();
    for (; ; ) {
        int c = getState();
      //判断是否此时有写锁。
        if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
            return false;
      //读锁的共享数量
        int r = sharedCount(c);
        if (r == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
      //尝试CAS
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            if (r == 0) {
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) {
                firstReaderHoldCount++;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null ||
                        rh.tid != LockSupport.getThreadId(current))
                    cachedHoldCounter = rh = readHolds.get();
                else if (rh.count == 0)
                    readHolds.set(rh);
                rh.count++;
            }
            return true;
        }
    }
}
final boolean tryWriteLock() {
    Thread current = Thread.currentThread();
  //获取状态
    int c = getState();
  //c!=0表示有线程持有锁;
    if (c != 0) {
      //获取独占锁(写锁)的数量;
        int w = exclusiveCount(c);
      //w=0,证明写锁为0,当前是读锁状态,写无法获取锁。
      //w!=0&当前线程不是独占线程,那么,证明是其它线程获取写锁,不能共用。
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
    }
  //正常情况,尝试CAS。
    if (!compareAndSetState(c, c + 1))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}
    //TODO 写线程持有的重入数
    public int getWriteHoldCount() {
        return sync.getWriteHoldCount();
    }
    //TODO 每个读线程持有的重入数
    public int getReadHoldCount() {
        return sync.getReadHoldCount();
    }

三、升降级

1、锁降级

​ ReentrantReadWriteLock支持锁降级,即从写锁降级变为读锁,但是使用过程仍然要注意:

class CachedData {
  Object data;
  volatile boolean cacheValid;
  final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

  public 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;
        }
        // 在释放写锁之前通过获取读锁降级写锁(注意此时还没有释放写锁)
        //在写锁没有释放的时候,先申请读锁,成功了再释放写锁,这个过程就称为锁降级。
        rwl.readLock().lock();
      } finally {
        rwl.writeLock().unlock(); // 释放写锁而此时已经持有读锁
      }
    }

    try {
      use(data);
    } finally {
      rwl.readLock().unlock();
    }
  }
}

锁降级的核心:先把持住当前的写锁,再获取读锁,最后释放写锁。如果是先释放读写,再获取写锁,那个过程并不是锁降级。

2、锁升级

​ ReentrantReadWriteLock是不支持锁升级的。

四、使用场景

使用读写锁的场景最好还是那种读频繁,写稀疏的场景。

标签:JUC,return,写锁,ReadWriteLock,读写,current,读锁,lock
来源: https://blog.csdn.net/rekingman/article/details/98886965