ReentrantLock 依赖关系如下图所示

非公平锁实现原理

ReentrantLock 默认采用非公平锁。

// ReentrantLock
public ReentrantLock() {
    sync = new NonfairSync();
}

加锁流程

ReentrantLock 的 lock 方法通过同步器的 lock 方法实现。

// ReentrantLock
public void lock() {
    sync.lock();
}

同步器的 lock 方法会先调用 initialTryLock() 方法,如果失败,则调用 acquire() 方法。

// Sync extends AbstractQueuedSynchronizer
final void lock() {
    if (!initialTryLock())
        acquire(1);
}
// AbstractQueuedSynchronizer
public final void acquire(int arg) {
    // 尝试获取锁
    if (!tryAcquire(arg))
        // 获取锁失败则加入的等待队列
        // null, 自定义参数, 非共享锁, 不可打断, 无时限, 时限 
        acquire(null, arg, false, false, false, 0L);
}

NonfairSync 的 initialTryLock 分两步:尝试获取锁、尝试重入。

NonfairSync 的 tryAcquire 方法被调用前,必定会调用 initialTryLock() 方法检查锁是否被当前线程持有,也即,调用 tryAcquire 方法时,锁必定未被当前线程持有。因此,当未有线程持有锁时,tryAcquire 才能尝试获取锁。

// NonfairSync extends Sync
static final class NonfairSync extends Sync {
    
    // 初次尝试获取锁
    final boolean initialTryLock() {
        Thread current = Thread.currentThread();
        // 通过CAS尝试获取锁
        if (compareAndSetState(0, 1)) {
            // 将锁的持有者设为当前线程
            setExclusiveOwnerThread(current);
            return true;
        } 
        // 尝试获取锁失败,判断锁的持有者是否为当前线程
        else if (getExclusiveOwnerThread() == current) {
            // 锁重入
            int c = getState() + 1;
            // 整数溢出
            if (c < 0) 
                throw new Error("Maximum lock count exceeded");
            // 设置锁的状态:state 表示重入次数
            setState(c);
            return true;
        } else
            return false;
    }

    // 非初次尝试获取锁
    protected final boolean tryAcquire(int acquires) {
        // 未有线程持有锁时,当前线程尝试获取锁
        if (getState() == 0 && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
}

AbstractQueuedSynchronizer 的 acquire 方法负责将线程加入等待队列,acquire 方法的主要执行步骤如下:

  1. 若非第一个等待线程且前驱存在,则检查当前节点前驱是否已被取消(前驱线程被取消)

    • 若已被取消,则需要从队尾开始,往前清理已取消的节点,进入下一轮循环。
    • 若未被取消,检查当前线程是否已成为第一个线程,若是,则自旋等待,进入下一轮循环
  2. 若是第一个等待线程或者前驱不存在,则尝试获取锁,获取锁成功则直接返回 1 表示获取锁成功,
  3. 准备将当前线程加入等待队列

    • 3.1 若等待队列未创建,则创建等待队列,进入下一轮循环
    • 3.2 否则,若节点未创建,则创建节点,进入下一轮循环
    • 3.3 否则,若节点信息未设置,则设置节点信息(包括将节点加入队尾),进入下一轮循环
    • 3.4 否则,若为第一个等待线程且自旋次数不为 0,提示 JVM 当前线程正在忙等,进入下一轮循环
    • 3.5 否则,若节点状态为 0(默认),则将节点状态设为 1(WAITING)
    • 3.6 否则,重置自旋自旋次数,当前线程陷入等待,被唤醒后将等待状态置为 0。
// AbstractQueuedSynchronizer
final int acquire(
    // 尝试获取锁的节点
    Node node, 
    // 自定义参数
    int arg, 
    // 是否为共享锁
    boolean shared,
    // 是否可打断
    boolean interruptible, 
    // 是否带时限
    boolean timed,
    // 时限
    long time
) {
    Thread current = Thread.currentThread();
    byte spins = 0, postSpins = 0;  
    boolean interrupted = false, first = false;
    Node pred = null;               
    
    for (;;) {
        // 检查前驱是否被取消
        if (
            // 非第一个等待线程
            !first && 
            // 前驱存在
            // 除非本方法被ConditionNode.await()方法调用,否则node初始必为null
            // 也即,此条件在第一轮循环必为false
            (pred = (node == null) ? null : node.prev) != null &&
            // 非第一个等待线程
            !(first = (head == pred))
        ) {
            if (pred.status < 0) {
                // 若前驱被取消,则需要清理队列中取消的线程
                // 此举是为确保给当前节点的前驱能唤醒当前节点
                cleanQueue(); 
                continue;
            } else if (pred.prev == null) {
                // 若前驱的前驱为null,说明当前线程是第一个线程
                // !first 和 pred.prev == null 之间,head的值被修改,导致当前线程成为第一个线程
                // 第一个线程自旋等待,以减少线程切换的开销
                Thread.onSpinWait();
                continue;
            }
        }
        // 尝试获取锁
        if (
            // 第一个线程 或者 前驱不存在
            first || pred == null
        ) {
            boolean acquired;
            // 尝试获取锁
            try {
                if (shared)
                    acquired = (tryAcquireShared(arg) >= 0);
                else
                    acquired = tryAcquire(arg);
            } catch (Throwable ex) {
                cancelAcquire(node, interrupted, false);
                throw ex;
            }
            // 获取锁成功
            if (acquired) {
                // 当前线程是第一个线程
                if (first) {
                    node.prev = null;
                    head = node;
                    pred.next = null;
                    node.waiter = null;
                    if (shared)
                        // 共享节点获取到锁之后需要唤醒下一个节点
                        signalNextIfShared(node);
                    if (interrupted)
                        // 独占节点获取到锁之后需要重新设置打断标记
                        current.interrupt();
                }
                return 1;
            }
        }
        // 尝试获取锁失败后,准备将线程加入等待队列
        Node t;
        if (
            // 等待队列未创建
            (t = tail) == null
        ) { 
            // 创建等待队列
            if (tryInitializeHead() == null)
                // 创建队列失败则调用tryAcquire尝试获取锁,失败则park(long)等待,重复
                return acquireOnOOME(shared, arg);
        } else if (
            // 节点未创建:创建节点
            node == null
        ) {
            try {
                // 创建节点
                node = (shared) ? new SharedNode() : new ExclusiveNode();
            } catch (OutOfMemoryError oome) {
                // 创建节点失败则调用tryAcquire尝试获取锁,失败则park(long)等待,重复
                return acquireOnOOME(shared, arg);
            }
        } else if (
            // 前驱未设置:设置节点信息
            pred == null
        ) {
            // 当前节点线程
            node.waiter = current;
            // 当前节点前驱(节点将由前驱唤醒)
            node.setPrevRelaxed(t);
            // 通过CAS将尾节点置为当前节点
            if (!casTail(t, node))
                // 失败则进入下一次循环
                node.setPrevRelaxed(null);
            else
                // 将当前节点加入队尾
                // 此时t仍指向原来的尾节点,当前节点已为实际尾节点
                t.next = node;
        } else if (
            // 第一个节点 自旋次数不为0
            first && spins != 0
        ) {
            // 自旋等待
            --spins;
            Thread.onSpinWait();
        } else if (
            // 当前节点状态为0(新建节点状态默认为0)
            node.status == 0
        ) {
            // 当前节点状态置为等待(WAITING=1)
            node.status = WAITING;  
        } else {
            // 到此处,节点已被创建,节点信息已被设置,节点状态已被设置
            long nanos;
            // 设置自旋次数
            spins = postSpins = (byte)((postSpins << 1) | 1);
            // 当前线程等待
            if (!timed)
                // 无时限等待
                LockSupport.park(this);
            else if ((nanos = time - System.nanoTime()) > 0L)
                // 有时限等待
                LockSupport.parkNanos(this, nanos);
            else
                // 等待超时
                break;
            // 线程被唤醒后,将状态设置为0
            node.clearStatus();
            // 被打断且可被打断
            if ((interrupted |= Thread.interrupted()) && interruptible)
                break;
        }
    }
    // (等待超时)或者(被打断且可被打断)则取消获取锁
    return cancelAcquire(node, interrupted, interruptible);
}

线程被唤醒后,再次尝试获取锁,若获取锁失败,不立即陷入等待,而是自旋,再次尝试获取锁,直到自旋次数耗尽。线程每次陷入等待前,会重置自旋次数,自旋次数为 2^k,其中,k 表示当前线程是第 k 陷入等待。注意,陷入等待是指执行 park() 方法,而不是指加入等待队列,加入等待队列的操作只会执行一次。

非公平体现在哪?先进先出不是公平的吗?阅读非公平同步器的 acquire() 源码可知,先将尝试获取锁,再将线程加入等待队列,也即,新来的线程可直接与等待队列中的线程竞争,准确地说,新来的线程可直接与等待队列中的第一个等待线程竞争,这导致新来的线程可能比等待队列中的线程先执行,这是不公平的。

检查线程是否被打断使用的是 Thread.interrupted() 方法,Thread.interrupted() 会清除打断标记,线程再次调用 LockSupport.park() 时仍能生效,Thread.isInterrupted() 不会清除打断标记,线程再次调用 LockSupport.park() 时无效。

如果线程(等待超时)或者(被打断且可被打断),则会取消尝试获取锁 cancelAcquire(),如果因为等待超时而导致取消尝试获取锁,则会保留打断标记。非公平同步器 NonfairSync 没有用到 cancelAcquire() 的返回值。

private int cancelAcquire(
    Node node, 
    boolean interrupted,
    boolean interruptible
) {
    // 修改节点信息
    if (node != null) {
        node.waiter = null;
        node.status = CANCELLED;
        // 清理队列
        if (node.prev != null)
            cleanQueue();
    }
    if (interrupted) {
        if (interruptible)
            // CANCELLED=0x80000000,负数
            return CANCELLED;
        else
            // 重新打断自己,将打断标记置为 true
            Thread.currentThread().interrupt();
    }
    return 0;
}

解锁流程

ReentrantLock 的 unlock 方法通过同步器的 release方法实现。

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

同步器的 release 方法会先调用 tryRelease 尝试释放锁,若释放成功则会唤醒下一个线程。

// AbstractQueuedSynchronizer
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        signalNext(head);
        return true;
    }
    return false;
}

同步器的 tryRelease 方法先修改锁的持有者(置为 null),再修改锁的状态。

// Sync extends AbstractQueuedSynchronizer
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 仅锁的持有者才可以释放锁
    if (getExclusiveOwnerThread() != Thread.currentThread())
        throw new IllegalMonitorStateException();
    // free=true 表示现在重入次数为0
    boolean free = (c == 0);
    // 重入次数为0则将线程持有者置为null
    if (free)
        setExclusiveOwnerThread(null);
    // 设置线程状态
    setState(c);
    return free;
}

同步器中的 signalNext 方法将等待队列中的第一个线程唤醒。

// AbstractQueuedSynchronizer
private static void signalNext(Node h) {
    Node s;
    if (h != null && (s = h.next) != null && s.status != 0) {
        // 将state设置为 ~WAITING
        s.getAndUnsetStatus(WAITING);
        // 唤醒下一个线程(等待队列中的第一个等待线程)
        LockSupport.unpark(s.waiter);
    }
}

可重入

阅读 NonfairSync 的源码可以发现,线程第一次尝试获取锁时,尝试将 state 由 0 置为 1 失败后,会检查锁的持有者是否是自己,若是,则累计重入次数,并返回 true,表示获取锁成功。

当锁的持有者为当前线程,当前线程再次尝试获取锁仍能成功,此即可重入。

// NonfairSync extends Sync
static final class NonfairSync extends Sync {
    // 初次尝试获取锁
    final boolean initialTryLock() {
        Thread current = Thread.currentThread();
        // 通过CAS尝试获取锁
        if (compareAndSetState(0, 1)) {
            // 将锁的持有者设为当前线程
            setExclusiveOwnerThread(current);
            return true;
        } 
        // 尝试获取锁失败,判断锁的持有者是否为当前线程
        else if (getExclusiveOwnerThread() == current) {
            // 锁重入
            int c = getState() + 1;
            // 整数溢出
            if (c < 0) 
                throw new Error("Maximum lock count exceeded");
            // 设置锁的状态:state 表示重入次数
            setState(c);
            return true;
        } else
            return false;
    }
    ...
}

阅读 Sync 的源码可以发现,释放锁时,会将重入次数减去 releases(实际为 1),仅当重入次数为 0 时,线程才将锁的持有者置为 null 并返回 true,表示锁已成功释放。

// Sync extends AbstractQueuedSynchronizer
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    // 仅锁的持有者才可以释放锁
    if (getExclusiveOwnerThread() != Thread.currentThread())
        throw new IllegalMonitorStateException();
    // free=true 表示现在重入次数为0
    boolean free = (c == 0);
    // 重入次数为0则将线程持有者置为null
    if (free)
        setExclusiveOwnerThread(null);
    // 设置线程状态
    setState(c);
    return free;
}

可打断

理解一下参数 interruptible

  • interruptible = true,获取锁的过程中允许被打断,立即响应打断,不再尝试获取锁。
  • interruptible = false,获取锁的过程中不允许被打断,不立即响应打断,继续尝试获取锁。
// AbstractQueuedSynchronizer
final int acquire(...) {
    for (;;) {
        ...
        {
            ...
            // 被打断且可被打断
            if ((interrupted |= Thread.interrupted()) && interruptible)
                break;
        }
    }
    // (等待超时)或者(被打断且可被打断)则取消获取锁
    return cancelAcquire(node, interrupted, interruptible);
}

公平锁的实现原理

通过 ReentrantLock 的有参构造可创建公平锁。

// ReentrantLock
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

FairSync 和 NonfairSync 的区别即为公平锁和非公平锁的区别,主要关注 initialTryLock 方法。

公平锁在初次尝试获取锁时,仅当锁未被占有且等待队列无等待线程时,当前线程才会立即尝试获取锁,否则将加入等待队列队尾。

// ReentrantLock
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    // 初次尝试获取锁
    final boolean initialTryLock() {
        Thread current = Thread.currentThread();
        int c = getState();
        // 锁未被占有
        if (c == 0) {
            // 等待队列无未取消的等待线程 且 获取锁成功
            if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
                // 将锁的持有者设为当前线程
                setExclusiveOwnerThread(current);
                return true;
            }
        } else if (getExclusiveOwnerThread() == current) {
            // 重入
            if (++c < 0) 
                throw new Error("Maximum lock count exceeded");
            setState(c);
            return true;
        }
        return false;
    }

    // 非初次尝试获取锁
    protected final boolean tryAcquire(int acquires) {
        if (
            // 锁未被占有
            getState() == 0 && 
            // 等待队列无未取消的等待线程
            !hasQueuedPredecessors() &&
            // 获取锁成功
            compareAndSetState(0, acquires)
        ) {
            // 将锁的持有者设为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
}

hasQueuedThread() 用于判断等待队列中是否未取消的等待线程。

// AbstractQueuedSynchronizer
public final boolean hasQueuedThreads() {
    for (Node p = tail, h = head; p != h && p != null; p = p.prev)
        // 存在未取消的线程
        if (p.status >= 0)
            return true;
    return false;
}

条件变量的实现原理

ReentrantLock 通过 newCondition() 方法创建条件变量,而 ReentrantLock 的 newCondition() 方法基于同步器的 newCondition() 方法。

// ReentrantLock
public Condition newCondition() {
    return sync.newCondition();
}

同步器的 newCondition() 方法会创建一个 ConditionObject 对象,即条件变量对象。

// Sync extends AbstractQueuedSynchronizer
final ConditionObject newCondition() {
    return new ConditionObject();
}

每个条件变量维护一个条件变量队列。

// ReentrantLock
public class ConditionObject implements Condition{
    // 头节点
    private transient ConditionNode firstWaiter;
    // 尾节点
    private transient ConditionNode lastWaiter;
}

线程的等待和唤醒可粗略理解为

  • 线程等待:先将线程从等待队列中移除,再将线程加入条件变量队列
  • 线程唤醒:先将线程从条件变量队列中移除,再将线程加入等待队列
  • 条件变量队列中的线程不能参与锁的竞争,而等待队列中的线程可以参与锁的竞争

加入条件变量队列的流程

// ReentrantLock
public class ConditionObject implements Condition{
    // 将线程加入条件变量队列
    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 创建节点,创建失败则自旋尝试获取锁
        ConditionNode node = newConditionNode();
        // 创建节点失败
        if (node == null)
            return;
        // 将当前线程加入条件变量队列
        // 当前线程释放持有的锁(会将当前线程从等待队列溢出)并唤醒等待队列下一个线程
        // 返回重入次数
        int savedState = enableWait(node);
        // 当前线程等待原因置为this,常用于调试
        LockSupport.setCurrentBlocker(this); // for back-compatibility
        boolean interrupted = false, cancelled = false, rejected = false;
        // !(可以尝试获取锁)
        while (!canReacquire(node)) {
            if (interrupted |= Thread.interrupted()) {
                if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
                    break;              // else interrupted after signal
            } else if ((node.status & COND) != 0) {
                // 陷入等待
                try {
                    if (rejected)
                        node.block();
                    else
                        ForkJoinPool.managedBlock(node);
                } catch (RejectedExecutionException ex) {
                    // ForkJoinPool线程池无法接收任务
                    rejected = true;
                } catch (InterruptedException ie) {
                    interrupted = true;
                }
            } else
                Thread.onSpinWait();
        }
        // 当前线程等待原因置为null
        LockSupport.setCurrentBlocker(null);
        // 重新参与竞争
        node.clearStatus();
        acquire(node, savedState, false, false, false, 0L);
        if (interrupted) {
            if (cancelled) {
                unlinkCancelledWaiters(node);
                throw new InterruptedException();
            }
            Thread.currentThread().interrupt();
        }
    }
}
// ConditionObject implements Condition
private ConditionNode newConditionNode() {
    int savedState;
    // 初始化等待队列头节点
    if (tryInitializeHead() != null) {
        try {
            return new ConditionNode();
        } catch (OutOfMemoryError oome) {
        }
    }
    // 发生OutOfMemoryError时才会往下执行
    // 当前线程未持有锁或者释放锁失败
    if (!isHeldExclusively() || !release(savedState = getState()))
        throw new IllegalMonitorStateException();
    // 当前线程暂停一段时间
    U.park(false, OOME_COND_WAIT_DELAY);
    // 自旋尝试获取锁
    acquireOnOOME(false, savedState);
    return null;
}
// ConditionObject implements Condition
private int enableWait(ConditionNode node) {
    // 当前线程持有锁才可以加入条件变量队列
    if (isHeldExclusively()) {
        // 设置节点信息
        node.waiter = Thread.currentThread();
        node.setStatusRelaxed(COND | WAITING);
        // 将节点加入条件变量队列
        ConditionNode last = lastWaiter;
        if (last == null)
            firstWaiter = node;
        else
            last.nextWaiter = node;
        lastWaiter = node;
        // 当前线程的重入次数
        int savedState = getState();
        // 释放锁返回重入次数
        if (release(savedState))
            return savedState;
    }
    // 未持有锁或者释放锁失败则抛出异常
    node.status = CANCELLED;
    throw new IllegalMonitorStateException();
}
// ConditionObject implements Condition
private boolean canReacquire(ConditionNode node) {
    Node p;
    // node和p的链接关系没有被破坏或者node在等待队列中
    return node != null && 
        (p = node.prev) != null &&
        (p.next == node || isEnqueued(node));
}

移出条件变量队列的流程

// ConditionObject implements Condition
public final void signal() {
    ConditionNode first = firstWaiter;
    // 持有锁的线程才能唤醒条件变量队列中的线程
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    else if (first != null)
        doSignal(first, false);
}
// ConditionObject implements Condition
private void doSignal(ConditionNode first, boolean all) {
    while (first != null) {
        ConditionNode next = first.nextWaiter;
        // 将条件变量队列中的第一个等待线程移除
        if ((firstWaiter = next) == null)
            lastWaiter = null;
         // 将刚从条件变量队列中移除的等待线程加入等待队列
        if ((first.getAndUnsetStatus(COND) & COND) != 0) {
            // 加入等待队列
            enqueue(first);
            // 不是唤醒所有线程
            if (!all)
                break;
        }
        // 由于线程被取消等原因可能导致线程无法加入等待队列
        // 此时尝试等待队列的下一个线程
        first = next;
    }
}
// ConditionObject implements Condition
final void enqueue(ConditionNode node) {
    if (node != null) {
        // 是否立即唤醒线程node
        boolean unpark = false;
        for (Node t;;) {
            if ((t = tail) == null && (t = tryInitializeHead()) == null) {
                // 等待队列为null且初始化头节点失败时,应立即唤醒线程node
                unpark = true;
                break;
            }
            // 设置前驱
            node.setPrevRelaxed(t);
            // 通过CAS操作将等待队列队尾置为node
            if (casTail(t, node)) {
                t.next = node;
                // 等待队列队尾被取消,则唤醒线程node,以清理队列
                if (t.status < 0)
                    unpark = true;
                break;
            }
        }
        // 立即唤醒线程node
        if (unpark)
            LockSupport.unpark(node.waiter);
    }
}

END

如果觉得本文对您有一点点帮助,欢迎点赞、转发加关注,这会对我有非常大的帮助,如果有任何问题,欢迎在评论区留言或者后台私信,咱们下期见!

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。


字节幺零二四
9 声望5 粉丝

talk is cheap, show me you code!