文章

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

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

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

ReentrantLock

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

1. 和Aqs的关系

2. 加锁流程

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

t1加锁成功后

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

t2加锁失败,会创建队列

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;

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。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //1. 此时t2上一个节点的waitStatus=0 int ws = pred.waitStatus; if (ws == Node.SIGNAL) returntrue; 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); } returnfalse; }

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

3. 解锁流程

  1. 解锁前

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

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; }

3.解锁后

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

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被唤醒,则if判断不成立,代码继续执行,再循环一次

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); } } }

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