文章

并发编程:ReentrantLock的加锁和解锁过程

并发编程:ReentrantLock的加锁和解锁过程

并发编程:ReentrantLock的加锁和解锁过程

ReentrantLock

  • 和Aqs的关系
  • 加锁流程
  • 解锁流程

1. 和Aqs的关系

ReentrantLock与AQS关系图

2. 加锁流程

第一个线程t1,第一次加锁,没有加锁之前 aqs(NonfairSync)的状态

AQS初始状态

t1加锁成功后

t1加锁成功后状态

t1加锁成功后AQS状态

第二个线程t2尝试加锁,如果加锁成功

t2加锁成功假设状态

t2加锁失败,会创建队列

t2加锁失败创建队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private Node addWaiter(Node mode) {
    // 1. 创建t2节点
    Node node = newNode(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    // 2. 还不存在队列,故队尾node为null,不进入if
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 3. 创建队列
    enq(node);
    return node;
}

private Node enq(final Node node) {
    for (; ; ) {
        // 1. 循环第一次进来,队列不存在,就初始化队列,所以走if
        Node t = tail;
        if (t == null) { // Must initialize
            // 2. 把new出来的node设置为第一个节点,这个node的Thread、prev、next都是null,waitStatus=0
            //再把head节点赋给tail节点
            if (compareAndSetHead(newNode()))
                tail = head;
        } else {
            // 3. 循环第二次进来,队列存在,走else
            // t2节点的前一个节点指向第一次进循环new出来的那个node
            node.prev = t;
            // 4. 如果队尾是new出来的node,就替换成t2节点
            if (compareAndSetTail(t, node)) {
                // 5.把new出来的节点的next指向t2节点,返回new出来的node,跳出循环
                t.next = node;
                return t;
            }
        }
    }
}

t == null队列还没有创建;

compareAndSetHead(new Node())创建一个Thread=null的head;

node.prev = t; t2节点的prev指向head;

t.next = node; head的next节点指向t2;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (; ; ) {
            // 1. 取出t2节点的上一个节点
            final Node p = node.predecessor();
            // 2. p是head节点,但是t2重新加锁失败,不会进if
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 3. 修改上一节点的waitStatus && park当前线程
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

后加入队列的node会将前一个node的waitStatus置为-1(标记为-1的节点有责任唤醒下一个节点),尾部waitStatus默认值是0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //1. 此时t2上一个节点的waitStatus=0
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //2. 通过cas将t2上一个节点的ws置为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

t2节点waitStatus修改

t2进入队列后状态

.第三个线程t3尝试加锁,并且t1没有释放锁,t2还在队列中

t3加入队列后状态

3. 解锁流程

  1. 解锁前

解锁前状态

  1. 解锁中
    • t1解锁,进入tryRealse方法

tryRelease方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected final boolean tryRelease(int releases) {
    //1-1,c=0
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        // aqs的当前线程变为null
        setExclusiveOwnerThread(null);
    }
    // 当前aqs的state变为0
    setState(c);
    return free;
}

t1解锁后AQS状态

3.解锁后

头节点存在, 状态为-1, 走unparkSuccessor方法

unparkSuccessor方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void unparkSuccessor(Node node) {
    // 1. ws=-1
    int ws = node.waitStatus;
    // 2. 先将第一个节点的ws置为0,防止其他线程使用第一个节点重复唤醒下一个节点(只有ws=-1的节点才有资格唤醒下一个节点)
    if (ws < 0) {
        compareAndSetWaitStatus(node, ws, 0);
    }
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev) {
            if (t.waitStatus <= 0) {
                s = t;
            }
        }
    }
    //3.第二个节点是t2不等于null,unpark唤醒t2
    if (s != null) {
        LockSupport.unpark(s.thread);
    }
}
  • t2被unpark唤醒,代码会走到t2被排队等待的代码,parkAndCheckInterrupt()返回false

t2被唤醒后状态1

t2被唤醒后状态2

t2被唤醒,则if判断不成立,代码继续执行,再循环一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (; ; ) {
            // 1. 定义t2的前一个节点
            final Node p = node.predecessor();
            // 2. t2的前一个节点是head,t2重新加锁成功,会进入if
            if (p == head && tryAcquire(arg)) {
                // 3. 设置head节点为t2,断开aqs指向旧head的链接;同时将t2节点的thread和prev都设置为null
                setHead(node);
                // 4. t2的前一个节点断开指向t2的链接,至此旧head节点所有与外部的引用和被引用全都断开,等待下一次jvm的垃圾回收
                p.next = null; // help GC
                failed = false;
                // 5. 返回false,跳出循环
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
                interrupted = true;
            }
        }
    } finally {
        if (failed) {
            cancelAcquire(node);
        }
    }
}

t2获取锁成功后状态1

t2获取锁成功后状态2

本文由作者按照 CC BY 4.0 进行授权