首先看看它的类图(拿来吧x黑马)

image-20220305002246641

可以知道ReentrantLock里面实现了一个AQS,叫Sync,但是它是抽象的,具体的分为公平锁与非公平锁。

非公平锁

默认的构造方法就是非公平锁。

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

加锁流程

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

再点进去看, 发现是ReentrantLock里面继承了AQS的抽象的Sync。看它的子类NonfairSync,可以看到它的加锁方法:

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

首先是使用CAS对state改变,它为0时无锁,为1是有锁。

成功,就设置独占锁为当前线程,加锁成功。

若失败,走到acquire(1)。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

这里呢,首先会让这个线程再次尝试获得锁,tryAcquire。如果成功(比如锁的线程释放了),那就会跳出这个判断。

如果不成功,就把它放到等待队列中,也就是调用acquireQueued的方法。

先说说它的tryAcquire,是尝试抢夺锁的,对应代码如下:

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 当前没有锁独占
    if (c == 0) {
        // 尝试抢夺
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 可重入锁,如果独占锁的线程还是当前线程
    else if (current == getExclusiveOwnerThread()) {
        // 改state值
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

尝试抢夺失败后,调用acquireQueued方法,在这里再次尝试抢夺锁。

不过在调用它之前,有一个addWaiter的方法,构造一个等待队列,有头、尾指针,还有一个虚结点Node(null)。以后添加元素,都往虚结点的后面加。

这里把Node加进去后,其实就是受NonfairSync管理的一个双向链表。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 这个循环会走多次
        for (;;) {
            final Node p = node.predecessor();
            // 第一次,如果自己是在head后面(第二位)
            // 会再次tryAcquire获得锁,成功就返回,失败就走下面
            // 第二次,再次尝试获得锁,如果还是-1,就往下
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 接上第一次,将前驱node的waitStatus改为-1,返回false
            // 接上第二次,已经改ws为-1了,然后走下面的方法
            // 就被park了
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 被唤醒了
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

waitStatus表示它有职责唤醒它的后续结点,因此在双向队列中,虚结点和其后面的结点(除了最后一个,它为0)的ws都为-1,

解锁流程

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

尝试解锁

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

如果尝试解决成功,那就让它唤醒下一个线程,然后结束。

unparkSuccessor就是找到队列中离head最近一个Node,unpark恢复运行。

protected final boolean tryRelease(int releases) {
	// 检测可重入的,-1,改变的只是锁计数
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        // 设置独占为null,解锁了
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

怎么尝试解锁 ?

tryRelease成功后,就把独占的owner设为null,并修改state变量,然后再走unparkSuccessor这个进行unpark操作。

那这个线程unpark之后,就回到加锁时的acquireQueued中,它被唤醒后,继续它的死循环,然后抢夺锁。

注意,虽然唤醒了链表中的下一个线程,但是它也有可能竞争失败的,因为它是非公平锁,有可能别的线程抢到了锁,这时它又要在循环中抢夺,失败的话再入链表。。。

可重入原理

其实就是修改state的值,加一或减一。

可以看看上面加解锁的nonfairTryAcquiretryRelease

不可打断原理与可打断原理

我认为是外部如果对线程进行unpark的话,只是改变它的标志,但是并不能真正地释放锁。它还是在AQS中,只有在获得锁后才知道自己打断了。

acquireQueued中,多次抢夺都抢不到锁,就会被打断

private final boolean parkAndCheckInterrupt() {
    // 等待。如果打断标志为true,park会失效
    LockSupport.park(this);
    // interrupted会清除打断标志
    return Thread.interrupted();
}

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                // 返回
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 只是改变标记,然后再走循环,还是会被park住,没有释放锁
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 如果打断状态为true
        selfInterrupt();
}

static void selfInterrupt() {
    // 重新产生一次中断 
    Thread.currentThread().interrupt();
}

公平锁原理

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 看看队列中有没有元素,如果没有
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

它不会去抢夺锁,而且看看队列中是否有元素,如果没有,才去拿锁,不然的话在队列中乖乖排队。

条件变量实现原理

每个条件变量对应一个等待队列,实现类ConditionObject。

它是一个双向链表,有firstWaiter和lastWaiter。进入队列的状态会被设置Node.CONDITION,值为-2。

移出队列后,进入等待线程,然后改状态。。。

都是链表的操作,学到了。