可重入锁 ReentrantLock 源码阅读

425 阅读3分钟
原文链接: mp.weixin.qq.com

带着问题阅读。

1. 可重入是怎么实现的?

AQS中的同步状态变量state,在 ReentrantLock 中表示锁被持有的次数,因为锁是独占的,也可以理解为锁定的深度。锁的可重入便是依赖该字段,下面以公平锁中的锁定过程为例说明(非公平锁也是类似的)。

  1. // ReentrantLock.FairSync.java

  2. protected final boolean tryAcquire(int acquires) {

  3.    final Thread current = Thread.currentThread();

  4.    int c = getState();

  5.    if (c == 0) {   // 首次取锁

  6.        if (!hasQueuedPredecessors() &&

  7.            compareAndSetState(0, acquires)) { // 如果等待队列中没有先到的线程,则尝试获取

  8.            setExclusiveOwnerThread(current);   // 成功,设置该锁的持有线程

  9.            return true;

  10.        }

  11.    }

  12.    else if (current == getExclusiveOwnerThread()) {  // 当前线程就是持有锁的线程,则直接增加计数

  13.        int nextc = c + acquires;

  14.        if (nextc < 0)     // int 溢出了, 这里也决定了递归获取锁的深度

  15.            throw new Error("Maximum lock count exceeded");

  16.        setState(nextc);

  17.        return true;

  18.    }

  19.    return false;

  20. }

2. 公平,非公平锁是怎么实现的?

ReentrantLock 实例化的时候可以配置公平争用策略,公平,非公平对应了 ReentrantLock 内部的两个AQS类:FairSync,NonfairSync。默认是非公平的。

  1. public ReentrantLock() {

  2.    sync = new NonfairSync();

  3. }

  4. public ReentrantLock(boolean fair) {

  5.    sync = fair ? new FairSync() : new NonfairSync();

  6. }

加锁,释放锁过程如下,其中释放锁过程是一样的。

二者的区别在于tryAcquire的时候逻辑不同。

公平锁在获取锁之前会判断是否有先到的线程在等待该锁(避免饥饿),实现为:

  1. protected final boolean tryAcquire(int acquires) {

  2.    final Thread current = Thread.currentThread();

  3.    int c = getState();

  4.    if (c == 0) { // 先看是否有等待者

  5.        if (!hasQueuedPredecessors() &&

  6.            compareAndSetState(0, acquires)) {

  7.            setExclusiveOwnerThread(current);

  8.            return true;

  9.        }

  10.    }

  11.    else if (current == getExclusiveOwnerThread()) {

  12.        int nextc = c + acquires;

  13.        if (nextc < 0)

  14.            throw new Error("Maximum lock count exceeded");

  15.        setState(nextc);

  16.        return true;

  17.    }

  18.    return false;

  19. }

  20. public final boolean hasQueuedPredecessors() {

  21.    // The correctness of this depends on head being initialized

  22.    // before tail and on head.next being accurate if the current

  23.    // thread is first in queue.

  24.    Node t = tail; // Read fields in reverse initialization order

  25.    Node h = head;

  26.    Node s;

  27.    return h != t &&

  28.        ((s = h.next) == null || s.thread != Thread.currentThread());

  29. }

而非公平锁直接进行强占。

  1. /**

  2. * Performs lock.  Try immediate barge(闯入), backing up to normal

  3. * acquire on failure.

  4. */

  5. final void lock() {

  6.    if (compareAndSetState(0, 1))    // 注意这里

  7.        setExclusiveOwnerThread(Thread.currentThread());

  8.    else

  9.        acquire(1);

  10. }

  11. protected final boolean tryAcquire(int acquires) {

  12.    return nonfairTryAcquire(acquires);

  13. }

  14. // tryLock 也会调用这个方法

  15. final boolean nonfairTryAcquire(int acquires) {

  16.    final Thread current = Thread.currentThread();

  17.    int c = getState();

  18.    if (c == 0) {

  19.        if (compareAndSetState(0, acquires)) {

  20.            setExclusiveOwnerThread(current);

  21.            return true;

  22.        }

  23.    }

  24.    else if (current == getExclusiveOwnerThread()) {

  25.        int nextc = c + acquires;

  26.        if (nextc < 0) // overflow

  27.            throw new Error("Maximum lock count exceeded");

  28.        setState(nextc);

  29.        return true;

  30.    }

  31.    return false;

  32. }