ReentrantReadWriteLock源码解读

1 篇文章 0 订阅
1 篇文章 0 订阅
                                       ReentrantReadWriteLock解读
  1. 读写锁简介
    有的时候对某些资源,读取次数远大于更新次数,但同时写也是必不可少的,这时候我我们可以考虑用读写锁

  2. 类图
    在这里插入图片描述
    在这里插入图片描述

  3. 主要类成员属性
    //提供读锁的内部类
    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;

//提供写锁的内部类
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;

//实现了AbstractQueuedSynchronizer抽象类,提供锁操作的同步机制
/** Performs all synchronization mechanics */
final Sync sync;			

/**
 * Synchronization implementation for ReentrantReadWriteLock.
 * Subclassed into fair and nonfair versions.
 * 为ReentrantReadWriteLock实现的抽象同步器,该类的子类分为公平和分公平版本
 */
 
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 6317671515068378041L;

       /*
 * Read vs write count extraction constants and functions.
 * Lock state is logically divided into two unsigned shorts:
 * The lower one representing the exclusive (writer) lock hold count,
 * and the upper the shared (reader) hold count.
 *下面会介绍提取读写锁的常量和和函数,锁状态也既就是AQS里的state变量,被拆成两部分,
 * int类型共32位二进制码, 低16位代表独占锁,高16位代表共享锁
 */
//区分共享锁的位移量
static final int SHARED_SHIFT   = 16;
//共享锁的基本操作单元
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
//最大数量的读或者写锁,当数量达到最大值,再次请求会抛出异常Maximum lock count exceeded
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
//锁状态与上独占锁模就获取到独占锁数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

/** Returns the number of shared holds represented in count
 * 把锁状态state 左移16位得到共享锁数量
 * */
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count
 * 锁状态与上独占锁模就获取到独占锁数量
 * */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
  1. 线程读锁持有器ThreadLocalHoldCounter
    /**

    • A counter for per-thread read hold counts.
    • Maintained as a ThreadLocal; cached in cachedHoldCounter
    • 为每个线程记录读锁数量的计数器
      */
      static final class HoldCounter {
      int count = 0;
      // Use id, not reference, to avoid garbage retention
      final long tid = getThreadId(Thread.currentThread());
      }

    /**

    • ThreadLocal subclass. Easiest to explicitly define for sake
    • of deserialization mechanics.
    • 把ThreadLocalHoldCounter把HoldCounter类封装为线程隔离的变量
      */
      static final class ThreadLocalHoldCounter
      extends ThreadLocal {
      public HoldCounter initialValue() {
      return new HoldCounter();
      }
      }

    /**

    • The number of reentrant read locks held by current thread.
    • Initialized only in constructor and readObject.
    • Removed whenever a thread’s read hold count drops to 0.
    • 当前线程持有的读锁数量
      */
      private transient ThreadLocalHoldCounter readHolds;
  2. tryRelease方法,尝试释放写锁

    //重写AQS的tryRelease,释放独占锁
    protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
    throw new IllegalMonitorStateException();
    //减去需要释放的数值
    int nextc = getState() - releases;
    //因为是重入锁,所以释放以后需要判断该线程是否完全释放了独占锁
    boolean free = exclusiveCount(nextc) == 0;
    //如果释放完成,设置当前独占锁线程为null
    if (free)
    setExclusiveOwnerThread(null);
    //因为持有写所,所以对state的操作是线程安全的,不需要额外的同步
    setState(nextc);
    return free;
    }

  3. tryAcquire尝试获取一定数量的写锁

    //尝试获取写锁
    protected final boolean tryAcquire(int acquires) {
    /*
    * Walkthrough:
    * 1. If read count nonzero or write count nonzero
    * and owner is a different thread, fail.
    * 2. If count would saturate, fail. (This can only
    * happen if count is already nonzero.)
    * 3. Otherwise, this thread is eligible for lock if
    * it is either a reentrant acquire or
    * queue policy allows it. If so, update state
    * and set owner.
    */
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount©;
    //已经有读锁或者写锁
    if (c != 0) {
    //如果有其他线程的读锁,或者其他线程持有写锁,返回false
    // (Note: if c != 0 and w == 0 then shared count != 0)
    if (w == 0 || current != getExclusiveOwnerThread())
    return false;
    //判断同一线程的写锁是否超过最大值。超过最大值抛出异常
    if (w + exclusiveCount(acquires) > MAX_COUNT)
    throw new Error(“Maximum lock count exceeded”);
    //同一线程锁重入,无需额外同步
    // Reentrant acquire
    setState(c + acquires);
    return true;
    }
    //读锁和写锁都没有被获取,如果是非公平锁writerShouldBlock返回false
    //然后看cas操作是否成功,如果成功则获取到写锁,如果是公平锁并且线程队列中已经有
    //线程在排队了,则返回false
    if (writerShouldBlock() ||
    !compareAndSetState(c, c + acquires))
    return false;
    //获取成功,直接设置当前线程读写锁的值
    setExclusiveOwnerThread(current);
    return true;
    }

    //尝试获取写锁
    protected final boolean tryAcquire(int acquires) {
    /*
    * Walkthrough:
    * 1. If read count nonzero or write count nonzero
    * and owner is a different thread, fail.
    * 2. If count would saturate, fail. (This can only
    * happen if count is already nonzero.)
    * 3. Otherwise, this thread is eligible for lock if
    * it is either a reentrant acquire or
    * queue policy allows it. If so, update state
    * and set owner.
    */
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount©;
    //已经有读锁或者写锁
    if (c != 0) {
    //如果有其他线程的读锁,或者其他线程持有写锁,返回false
    // (Note: if c != 0 and w == 0 then shared count != 0)
    if (w == 0 || current != getExclusiveOwnerThread())
    return false;
    //判断同一线程的写锁是否超过最大值。超过最大值抛出异常
    if (w + exclusiveCount(acquires) > MAX_COUNT)
    throw new Error(“Maximum lock count exceeded”);
    //同一线程锁重入,无需额外同步
    // Reentrant acquire
    setState(c + acquires);
    return true;
    }
    //读锁和写锁都没有被获取,如果是非公平锁writerShouldBlock返回false
    //然后看cas操作是否成功,如果成功则获取到写锁,如果是公平锁并且线 程队列中已经有
    //线程在排队了,则返回false
    if (writerShouldBlock() ||
    !compareAndSetState(c, c + acquires))
    return false;
    //获取成功,直接设置当前线程读写锁的值
    setExclusiveOwnerThread(current);
    return true;
    }

  4. tryReleaseShared尝试释放读锁

    //尝试释放共享锁
    protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    //如果请求释放锁的线程是第一个获取读锁的线程.否则看下是否是最近刚获取了读锁的线程
    if (firstReader == current) {
    //如果只获取了一次读锁,直接释放,否则减1
    // assert firstReaderHoldCount > 0;
    if (firstReaderHoldCount == 1)
    firstReader = null;
    else
    firstReaderHoldCount–;
    } else {
    HoldCounter rh = cachedHoldCounter;
    //如果不是最近获取到读锁的线程需要释放读锁,那需要获取当前线程持有的读锁数量
    if (rh == null || rh.tid != getThreadId(current))
    rh = readHolds.get();
    int count = rh.count;
    //小于等于1直接从线程变量中把HoldCounter对象移除
    if (count <= 1) {
    readHolds.remove();
    //如果小于等于0,出现了异常情况,需要抛出异常
    if (count <= 0)
    throw unmatchedUnlockException();
    }
    //释放读锁
    –rh.count;
    }
    //释放掉当前线程的持有的一个读锁之后,需要用更改state,这也是为何需要for循环的原因
    for (;? {
    int c = getState();
    //减去state变量代表的读锁单元
    int nextc = c - SHARED_UNIT;
    if (compareAndSetState(c, nextc))
    // Releasing the read lock has no effect on readers,
    // but it may allow waiting writers to proceed if
    // both read and write locks are now free.
    //当读写锁都没有的时候返回true
    return nextc == 0;
    }
    }

  5. tryAcquireShared 尝试获取共享锁

    //尝试获取共享锁
    protected final int tryAcquireShared(int unused) {
    /*
    * Walkthrough:
    * 1. If write lock held by another thread, fail.
    * 2. Otherwise, this thread is eligible for
    * lock wrt state, so ask if it should block
    * because of queue policy. If not, try
    * to grant by CASing state and updating count.
    * Note that step does not check for reentrant
    * acquires, which is postponed to full version
    * to avoid having to check hold count in
    * the more typical non-reentrant case.
    * 3. If step 2 fails either because thread
    * apparently not eligible or CAS fails or count
    * saturated, chain to version with full retry loop.
    /
    Thread current = Thread.currentThread();
    int c = getState();
    //如果其他线程持有写锁,直接返回失败
    if (exclusiveCount© != 0 &&
    getExclusiveOwnerThread() != current)
    return -1;
    int r = sharedCount©;
    //读锁不应该阻塞,并且读锁没有到底最大值,并且通过了cas更新锁状态,加上读锁单元
    if (!readerShouldBlock() &&
    r < MAX_COUNT &&
    compareAndSetState(c, c + SHARED_UNIT)) {
    //如果读锁等于o,则对第一个持有读锁信息的变量赋值,如firstReader,firstReaderHoldCount
    if (r == 0) {
    firstReader = current;
    firstReaderHoldCount = 1;
    } else if (firstReader == current) { //当前线程的读锁加1
    firstReaderHoldCount++;
    } else {
    //判断当前的线程是不是最新获取读锁的线程,如果不是再获取当前线程的本地变量HoldCounter
    HoldCounter rh = cachedHoldCounter;
    if (rh == null || rh.tid != getThreadId(current))
    cachedHoldCounter = rh = readHolds.get();
    else if (rh.count == 0)
    readHolds.set(rh);
    rh.count++; //线程所占读锁加1
    }
    return 1;
    }
    /
    *
    * Full version of acquire for reads, that handles CAS misses
    * and reentrant reads not dealt with in tryAcquireShared.
    */
    //处理cas操作失败,或者读锁被阻塞了,应该调用下面的方法进行读锁获取
    return fullTryAcquireShared(current);
    }

  6. fullTryAcquireShared方法是对tryAcquireShared的补充,实际上方法体里的重要内容和上面的方法差不多,这儿就不写注释了。通过无线循环来获取锁

    /**
    * Full version of acquire for reads, that handles CAS misses
    * and reentrant reads not dealt with in tryAcquireShared.
    /
    final int fullTryAcquireShared(Thread current) {
    /

    * This code is in part redundant with that in
    * tryAcquireShared but is simpler overall by not
    * complicating tryAcquireShared with interactions between
    * retries and lazily reading hold counts.
    */
    //用于获取线程局部变量
    HoldCounter rh = null;
    for (;? {
    int c = getState();
    if (exclusiveCount© != 0) {
    if (getExclusiveOwnerThread() != current)
    return -1;
    // else we hold the exclusive lock; blocking here
    // would cause deadlock.
    } else if (readerShouldBlock()) {
    // Make sure we’re not acquiring read lock reentrantly
    //如果获取到读锁的是当前线程,firstReaderHoldCount应该是大于0的
    if (firstReader == current) {
    // assert firstReaderHoldCount > 0;
    } else {
    if (rh == null) {
    rh = cachedHoldCounter;
    if (rh == null || rh.tid != getThreadId(current)) {
    rh = readHolds.get();
    if (rh.count == 0)
    readHolds.remove();
    }
    }
    if (rh.count == 0)
    return -1;
    }
    }
    if (sharedCount© == MAX_COUNT)
    throw new Error(“Maximum lock count exceeded”);
    if (compareAndSetState(c, c + SHARED_UNIT)) {
    if (sharedCount© == 0) {
    firstReader = current;
    firstReaderHoldCount = 1;
    } else if (firstReader == current) {
    firstReaderHoldCount++;
    } else {
    if (rh == null)
    rh = cachedHoldCounter;
    if (rh == null || rh.tid != getThreadId(current))
    rh = readHolds.get();
    else if (rh.count == 0)
    readHolds.set(rh);
    rh.count++;
    cachedHoldCounter = rh; // cache for release
    }
    return 1;
    }
    }
    }

  7. tryWriteLock尝试获取独占锁

/**
 * Performs tryLock for write, enabling barging in both modes.
 * This is identical in effect to tryAcquire except for lack
 * of calls to writerShouldBlock.
 */
//尝试获取写锁
final boolean tryWriteLock() {
    Thread current = Thread.currentThread();
    int c = getState();
    //如果获取锁状态不为0,则走if语句体,进行详细判断
    if (c != 0) {
        int w = exclusiveCount(c);
        //第一中情况有其他线程还在读数据,不能更改,所以不能或者到读锁
        //第二种情况是有其他线程已经获取了写锁,即独占锁
        if (w == 0 || current != getExclusiveOwnerThread())
            return false; //满足上面两种情况之一返回false
        //写锁重入已是最大值,不可再获取。抛出异常
        if (w == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
    }
    //cas失败证明锁状态已经被其他线程修改
    if (!compareAndSetState(c, c + 1))
        return false;
    //设置获取到独占锁的线程
    setExclusiveOwnerThread(current);
    return true;
}
  1. getReadHoldCount()查询当前线程的重入锁数量

//查询当前线程的重入锁数量
final int getReadHoldCount() {
//如果读锁数量为0,那肯定当前线程的读锁肯定为0
if (getReadLockCount() == 0)
return 0;
//如果当前线程是第一个获取读锁的线程,则返回其占有的读锁数量
Thread current = Thread.currentThread();
if (firstReader == current)
return firstReaderHoldCount;
//如果不是最近获取读锁的线程,则从线程局部变量里取 HoldCounter
HoldCounter rh = cachedHoldCounter;
if (rh != null && rh.tid == getThreadId(current))
return rh.count;

    int count = readHolds.get().count;
    if (count == 0) readHolds.remove();
    return count;
}
  1. 静态内部类写锁WriteLock

/**
* The lock returned by method {@link ReentrantReadWriteLock#writeLock}.
*/
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;

    /**
     * Constructor for use by subclasses
     *
     * @param lock the outer lock object
     * @throws NullPointerException if the lock is null
     */
    protected WriteLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
    }

在这里插入图片描述
加锁和释放锁都是调用AQS(AbstractQueuedSynchronizer)子类来实现的
public void lock() {
sync.acquire(1);
}

public void unlock() {
sync.release(1);
}

静态内部类ReadLock和WriteLock 是相同的道理,具体同步实现都是由AQS的子类实现
在这里插入图片描述

ReentrantReadWriteLockJava中的一个实现,它允许多个线程同时取共享资源,但只允许一个线程入共享资源。它是可重入的,也就是说,同一个线程可以多次获取,而不会出现死。 ReentrantReadWriteLock源码实现比较复杂,主要包括两个内部类:Sync和FairSync。Sync是ReentrantReadWriteLock的核心实现,它维护了的状态,以及等待队列。FairSync是Sync的子类,它实现了公平的逻辑。 ReentrantReadWriteLock读写锁是通过Sync类中的state字段实现的。state的高16位表示的持有数量,低16位表示的持有数量。当一个线程获取时,它会增加state的高16位;当一个线程获取时,它会增加state的低16位。当一个线程释放时,它会减少相应的state值。 ReentrantReadWriteLock的等待队列是通过Sync类中的readQueues和writeQueue字段实现的。当一个线程请求时,它会被加入到相应的等待队列中。当一个线程释放时,它会唤醒等待队列中的一个线程。 ReentrantReadWriteLock的公平实现是通过FairSync类中的tryAcquireShared和tryAcquire方法实现的。tryAcquireShared方法用于获取,它会先检查等待队列中是否有请求,如果有,则返回失败;否则,它会尝试获取。tryAcquire方法用于获取,它会先检查等待队列中是否有请求,如果有,则返回失败;否则,它会尝试获取。 总的来说,ReentrantReadWriteLock是一个非常实用的实现,它可以提高多线程程序的并发性能。但是,由于它的实现比较复杂,使用时需要注意避免死和竞争条件等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值