AQS ReentrantLock ReentrantReadWriteLock CountDownLatch源码阅读

编程入门 行业动态 更新时间:2024-10-09 20:28:46

AQS ReentrantLock ReentrantReadWriteLock CountDownLatch<a href=https://www.elefans.com/category/jswz/34/1770099.html style=源码阅读"/>

AQS ReentrantLock ReentrantReadWriteLock CountDownLatch源码阅读

1. AQS源码阅读

1.1 AQS简介

AbstractQueuedSynchronizer 简称AQS,是实现JUC包中各种锁的关键,此类是一个模板类,具体的ReentrantLockCountDownLatchReadWriteLock等等都是自己去实现里边变量的使用规则。

各种类型的锁都有自己的锁类型信息

  • 比如ReadWriteLock就肯定会有当前的锁状态是读锁模式还是写锁模式

    static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式
    static final Node EXCLUSIVE = null;    // 当前锁状态是 排它锁(写锁)模式
    

这些所有的信息加上线程本身被封装成一个内部类对象 Node

每个Node都有指向前驱节点和后继节点的指针,每个节点将有关线程的某些控制信息保存在其节点的前身中。

1.2 AQS内部类详解

1.2.1 Node

  • 用作等待队列
static final class Node {static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式static final Node EXCLUSIVE = null;    // 当前锁状态是 排它锁(写锁)模式static final int CANCELLED =  1;		// 表示线程被取消(执行过interrupt()方法或者 timeout )static final int SIGNAL    = -1;		// 等待队列中 此线程后边的节点需要被 notify()、或者 LockSupport.unpark()【公平锁?】static final int CONDITION = -2;		// 此线程在一个 condition 等待队列中static final int PROPAGATE = -3;   		// 暂时不知道是啥用(即使是头结点是共享锁模式好像也到排它锁的时候停止了)/* 存储一个上边的某一个值,表示当前节点的状态 */volatile int waitStatus;// 当前节点的前一个节点,用来监视前一个节点的状态(如果前一个节点被取消,自己连接到前一个节点的前一个节点)volatile Node prev;// 当前节点的后一个节点,当这个节点当做头结点释放锁的时候,通知下一个节点volatile Node next;// 节点中保存的 线程volatile Thread thread;// 指向一个 condition等待队列Node nextWaiter;
}

1.2.2 ConditionObject

  • new ReentrantLock().newCondition()创建的对象
public class ConditionObject implements Condition, java.io.Serializable {// condition等待队列的第一个节点private transient Node firstWaiter;// condition等待队列的最后一个节点private transient Node lastWaiter;
}

1.3 非公平模式图解

1.4 公平模式图解

1.5 线程被打断

1.6 进入Condition等待队列

1.6.1

1.6.2

1.6.3 终于用上了nextWaiter指针

1.7 AQS类变量详解

// 表示锁当前的状态,初始为0, ReentrantLock.lock()之后数字加1,表示已经上锁
private volatile int state;
// 等待队列的头结点(head节点初始化的时候,waitStatus会被赋值为0)
private transient volatile Node head;
// 等待队列的尾巴节点
private transient volatile Node tail;// 通过 VarHander 直接获取上边三个变量的地址,直接使用变量地址来进行CAS操作
private static final VarHandle STATE;
private static final VarHandle HEAD;
private static final VarHandle TAIL;
static {try {/* handle 绑定到一个了个类的属性上,通过位于类的类型,变量的名字 和 变量本身的类型 */MethodHandles.Lookup l = MethodHandles.lookup();STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);} catch (ReflectiveOperationException e) {throw new ExceptionInInitializerError(e);}
}

1.8 插入到AQS队列

插入到AQS队列中只需要对当前的tail节点执行一次CAS操作

prev主要用于处理线程取消(线程超时或者被打断)的情况。如果取消某个节点,则其后继节点(通常)会重新链接到未取消的前任节点。

加入队列就是原子操作更新tail节点的next对象

2 ReentrantLockAQS

2.1 lock()

获得锁

CAS更改state的值,如果当前为0,线程1给他加到了1,表示线程1抢到了这把锁,其余线程进入等待队列,CAS更新tail节点的next指针

final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();// 获取state的值int c = getState();if (c == 0) {// 如果为0,CAS替换为1if (compareAndSetState(0, acquires)) {// 替换成功设置自己为独占线程setExclusiveOwnerThread(current);return true;}}// 没有CAS成功则表示已有线程获得锁,// 然后看看线程是不是自己(可重入)else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 把 state 的值 ++ setState(nextc);return true;}return false;
}// 如果获取锁失败(没抢到 + 自己也不是独占线程)
// 就要把自己加到等待队列中,等待队列的实现使用一个AQS的内部类实现,Node,
// 把锁模式设置为独占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))private Node addWaiter(Node mode) {Node node = new Node(mode);for (;;) {// CAS死循环往队尾加Node oldTail = tail;if (oldTail != null) {// 用VarHandle给普通变量做CAS操作,把新申请的节点的pre设置为尾巴节点node.setPrevRelaxed(oldTail);// 再用 CAS 给尾巴的 next 设置 新nodeif (compareAndSetTail(oldTail, node)) {oldTail.next = node;return node;}} else {// 当前节点时第一个进队等待的节点,初始化等待队列initializeSyncQueue();}}
}final boolean acquireQueued(final Node node, int arg) {boolean interrupted = false;try {for (;;) {// 死循环,查看自己的前一个节点是否变成了头结点final Node p = node.predecessor();// 如果不是头结点直接短路,// 是头结点就开始尝试获得锁if (p == head && tryAcquire(arg)) {// 获取锁成功,就把自己设置为头结点setHead(node);// 上一个已经执行完成,上一个出队的时候把他的next置位空p.next = null; // help GCreturn interrupted;}// 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?// 第一次调用此方法之后前驱节点的值肯定被替换为了 -1 , 第二次调用的时候以true返回,进入ifif (shouldParkAfterFailedAcquire(p, node))interrupted |= parkAndCheckInterrupt(); // 调用lockSupport.park()阻塞线程}} catch (Throwable t) {cancelAcquire(node);if (interrupted)selfInterrupt();throw t;}
}// 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;// 前一个节点的状态是 -1   // 等待队列中 此线程后边的节点需要被 notify()if (ws == Node.SIGNAL)return true;// 前驱节点已经被取消(打断或者超时)if (ws > 0) {do {// 我就得找到没有被取消的前一个,看看他的状态node.prev = pred = pred.prev;} while (pred.waitStatus > 0);// 并把找到的没取消的next指针指向自己pred.next = node;} else {// 前一个线程的状态肯定是 0 或者 PROPAGATE = -3;, 用CAS替换为 -1predpareAndSetWaitStatus(ws, Node.SIGNAL);}return false;
}

可重入

可重入实现为给state属性值往上加1

公平性

只有state属性再次为0的时候表示线程1彻底释放了锁资源,就可以通过上述过程来选择

  • 非公平:新来的线程还在自旋状态的时候可以争抢锁资源,队列中的节点只激活头结点的下一个

  • 公平:新来的线程也不可以抢锁

2.2 trylock()

尝试获取锁,获取不到锁或者过了超时时间就把自己设置为取消状态,并且更新前后节点的指向关系

curNode.next.pre = curNode.pre

curNode.pre.next = curNode.next

private boolean doAcquireNanos(int arg, long nanosTimeout)throws InterruptedException {if (nanosTimeout <= 0L)return false;// 设置线程超时时间final long deadline = System.nanoTime() + nanosTimeout;final Node node = addWaiter(Node.EXCLUSIVE);try {for (;;) {// 这里跟普通获取锁的逻辑一样final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCreturn true;}// 查看自己还能活多久nanosTimeout = deadline - System.nanoTime();// 已经超时,取消获取锁if (nanosTimeout <= 0L) {// 已经超时,取消获取锁,把自己取消,更改自己在等待队列中的前后关系cancelAcquire(node);return false;}if (shouldParkAfterFailedAcquire(p, node) &&// 还有没有必要把线程阻塞?有可能阻塞过程执行的时间都要比他过期时间短,那我阻塞他还有什么用呢?// 不如留着他自己过期的了nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)LockSupport.parkNanos(this, nanosTimeout);if (Thread.interrupted())throw new InterruptedException();}} catch (Throwable t) {cancelAcquire(node);throw t;}
}

2.3 lockInterruptibly()

节点中线程可以被打断,打断之后的线程也设置为取消状态,并且更新节点的指向关系

private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCreturn;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())// 如果在其他线程中调用了该线程的  interrupt 方法,parkAndCheckInterrupt 就会返回true//从而跳入 if  抛出异常throw new InterruptedException();}} catch (Throwable t) {cancelAcquire(node);throw t;}
}private final boolean parkAndCheckInterrupt() {LockSupport.park(this);// 如果在其他线程中调用了该线程的  interrupt 方法,就会返回truereturn Thread.interrupted();
}

2.4 condition.await()


public final void await() throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();// 新建一个 waitStatus = -2 (CONDITION)的节点Node node = addConditionWaiter();// 释放锁,并把现在已经加到队列中的线程激活int savedState = fullyRelease(node);int interruptMode = 0;// node 是 CONDITION状态 进入while, 阻塞线程while (!isOnSyncQueue(node)) {LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}// acquireQueued 把自己阻塞if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;if (node.nextWaiter != null) // clean up if cancelledunlinkCancelledWaiters();if (interruptMode != 0)reportInterruptAfterWait(interruptMode);
}private Node addConditionWaiter() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node t = lastWaiter;// If lastWaiter is cancelled, clean out.// lastWaiter 上一次被加到condition等待队列的节点// t 为 空,则当前condition中没有节点等待,或者上一个等待节点的 waitStatus 已经被改变 (被取消)if (t != null && t.waitStatus != Node.CONDITION) {// 删除上一个节点,并继续把t设置为还在condition等待队列中的节点unlinkCancelledWaiters();t = lastWaiter;}// 新建Node node = new Node(Node.CONDITION);// 等待队列为空的话,自己就是第一个if (t == null)firstWaiter = node;elset.nextWaiter = node; // 不为空的话就把自己连接在上一个后边lastWaiter = node; // 自己是刚刚加进来最后等待的节点return node;
}// 调用await() 的时候释放锁,激活正在等待对列的线程
final int fullyRelease(Node node) {try {int savedState = getState();// 调用await() 的时候释放锁,激活正在等待对列的线程if (release(savedState))return savedState;throw new IllegalMonitorStateException();} catch (Throwable t) {node.waitStatus = Node.CANCELLED;throw t;}
}// 调用await() 的时候释放锁,激活正在等待对列的线程public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)// 调用await() 的时候释放锁,激活正在等待对列的线程unparkSuccessor(h);return true;}return false;}private void unparkSuccessor(Node node) {// ... 过滤代码(有可能头上的节点已经被取消,从队列头开始找到第一个没有取消的节点)// unpark一个线程,这个线程不是某个等待队列中找的,而是依照在创建线程节点时的先后顺序// 挑一个可以激活的进行激活操作,让后者获得此时的锁,// 如果后者也会因为当前的系统状态阻塞,则继续park自己向后激活(使用的next指针向后找)// condition 中使用的指针是 nextWaiterif (s != null)LockSupport.unpark(s.thread);
}

2.4 condition.signal()

public final void signal() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;// 等待队列不为空,叫醒第一个线程if (first != null)doSignal(first);
}private void doSignal(Node first) {do {if ( (firstWaiter = first.nextWaiter) == null)lastWaiter = null;first.nextWaiter = null;// 叫醒等待队列中的第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程} while (!transferForSignal(first) &&(first = firstWaiter) != null);
}// 叫醒第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程
final boolean transferForSignal(Node node) {/** If cannot change waitStatus, the node has been cancelled.*/if (!nodepareAndSetWaitStatus(Node.CONDITION, 0))return false;/** Splice onto queue and try to set waitStatus of predecessor to* indicate that thread is (probably) waiting. If cancelled or* attempt to set waitStatus fails, wake up to resync (in which* case the waitStatus can be transiently and harmlessly wrong).*/Node p = enq(node);int ws = p.waitStatus;if (ws > 0 || !ppareAndSetWaitStatus(ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;
}

2.4 condition.signalAll()

public final void signalAll() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;// 第一个等待节点不为空,叫醒所有线程// 循环叫醒所有线程,调用 LockSupport.unparkif (first != null)doSignalAll(first);
}private void doSignalAll(Node first) {// 因为此时要把所有线程叫醒了,所以把这两个变量置位空lastWaiter = firstWaiter = null;do {// 循环叫醒该等待队列中的所有线程,调用 LockSupport.unpark// 加到condition中调用的方法中 给 nextWaiter 赋值就是加到condition中的线程// 虽然他们都有next指针,但是next指针是说他们创建节点时的顺序Node next = first.nextWaiter;first.nextWaiter = null;transferForSignal(first);first = next;} while (first != null);
}final boolean transferForSignal(Node node) {/** If cannot change waitStatus, the node has been cancelled.*/if (!nodepareAndSetWaitStatus(Node.CONDITION, 0))return false;/** Splice onto queue and try to set waitStatus of predecessor to* indicate that thread is (probably) waiting. If cancelled or* attempt to set waitStatus fails, wake up to resync (in which* case the waitStatus can be transiently and harmlessly wrong).*/Node p = enq(node);int ws = p.waitStatus;if (ws > 0 || !ppareAndSetWaitStatus(ws, Node.SIGNAL))LockSupport.unpark(node.thread); // 给线程unparkreturn true;
}

3. ReentrantReadWriteLock与AQS

3.1 特性

  • AQS 的状态state是32位,读锁用高16位,表示持有读锁的线程数(sharedCount),写锁低16位,表示写锁的重入次数 (exclusiveCount)。

  • 状态值为 0 表示锁空闲。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZNe27oy-1613741042381)(md_imgs/ReentrantReadWriteLock & AQS.png)]

3.2 readLock.lock()

看完源码好像只有叫醒了队列中的后继节点,readLock可以直接(接下

public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);
}

接上)因为有读线程持有锁而直接跳过判断,返回业务代码直接执行。

  • 此场景只适用于已经有读线程持有锁,后续读线程来到的时候可以直接跳过判断。

  • 但对于读线程已经在等待队列中,就通过共享锁的设置叫醒后继节点,但是前一个节点还没执行结束,就已经把它挤出了队列,只要反正只要已经被挤出的线程能调用unlock方法就行。

  • 下一个节点也是如此直接把自己设置为头结点,挤出上一个头结点,然后继续往下唤醒。一直唤醒到排它锁节点,就得让刚刚挤出的所有线程执行完毕才可以继续执行排他线程代码。

3.3 小实验

/** @Author 郭学胤* @University 深圳大学* @Description* @Date 2021/2/8 19:28*/import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class L34_ReadWriteLock {static ReentrantLock reentrantLock = new ReentrantLock();static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();static int num;static Lock readLock = readWriteLock.readLock();static Lock writeLock = readWriteLock.writeLock();public static void main(String[] args) throws InterruptedException {L34_ReadWriteLock test = new L34_ReadWriteLock();/* 使用读写锁,读线程可以同时进行 */Runnable task1 = () -> test.read(readLock);Runnable task2 = () -> test.write(writeLock, new Random().nextInt(50));/** 首先启动4个读线程,每个读线程都是 exclusive* 每个读线程睡1s钟,三个执行完需要 4 秒钟** E -> E -> E -> E* 队列状态如上所示* */createWriteThread(task2, 4, "write");/** 主线程睡1s,保证现在是读线程在占着锁** */Thread.sleep(1000);/** 启动 10 个读线程,因为此时是 写线程在占用锁,* 所以读线程已经加到了队列中* 此时前边三个写线程全部执行完至少还剩 3s** E -> E -> E -> R -> ... -> R* */createReadThread(task1, 10, "read");Thread.sleep(1000);/** 再启动 3 个 写线程,前三个中的某个写线程还没有释放锁* 所以这三个线程也会加入队列* 此时前边三个写线程全部执行完至少还剩 2s** E -> E -> E -> R -> ... -> R -> E -> E -> E* */createWriteThread(task2, 3, "write");/** 主线程睡1s,保证现在是读线程在占着锁* 此时前边三个写线程全部执行完至少还剩 1s* */Thread.sleep(1000);/** 启动 10 个读线程,因为此时是 写线程在占用锁,* 所以读线程已经加到了队列中** E -> E -> E -> R -> ... -> R -> E -> E -> E -> R -> R ....* */createReadThread(task1, 10, "read");/** 最后的执行顺序,请大家自己打印看* 前四个 写锁* 十个 读锁* 三个 写锁* 十个 读锁* */}public void read(Lock lock) {try {lock.lock();Thread.sleep(1000);System.out.println("read over");} catch (Exception e) {} finally {lock.unlock();}}public void write(Lock lock, int val) {try {lock.lock();Thread.sleep(1000);num = val;System.out.println("write over");} catch (Exception e) {} finally {lock.unlock();}}public static void createWriteThread(Runnable task2, int i2, String write) {for (int i = 0; i < i2; i++) {new Thread(task2, write + i).start();}}public static void createReadThread(Runnable task1, int i2, String read) {for (int i = 0; i < i2; i++) {new Thread(task1, read + i).start();}}
}

实验结论分析

结论一
  • 每次读锁获得锁之后并不是把队列中所有的读锁unpark
  • 读锁变成头结点之后获取到锁之后,unpark下一个读锁的节点
  • 下一个读锁的节点获取锁之后,把之前头结点挤出队列,把自己设置为头节点,然后继续往下unpark下一个读锁节点
  • 当下一个是写锁节点的时候,仍然unpark下一个节点,但是写锁节点暂时获取不到锁资源,需要等待所有的读锁释放资源
结论二
  • 非公平锁只是说新来的线程,仍在自旋中的线程可以尝试获取锁
  • 一旦加到了队列中,就算是非公平锁也仍然需要前驱节点unpark

3.4 源码阅读

3.4.1 readLock.lock()

// 读锁获取,共享锁public void lock() {sync.acquireShared(1);}private void doAcquireShared(int arg) {// 先给自己注册共享模式的节点final Node node = addWaiter(Node.SHARED);boolean interrupted = false;try {for (;;) {// 获取前一个节点是否是头结点,如果前一个节点时头节点final Node p = node.predecessor();if (p == head) {// 开始获取共享锁// 如果获取共享锁成功就开始int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCreturn;}}// unpark之后,继续forif (shouldParkAfterFailedAcquire(p, node))interrupted |= parkAndCheckInterrupt();}} catch (Throwable t) {cancelAcquire(node);throw t;} finally {if (interrupted)selfInterrupt();}
}protected final int tryAcquireShared(int unused) {Thread current = Thread.currentThread();int c = getState();// 先查看当前有无排它锁,有的话直接返回 -1,说明获取共享锁失败if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current)return -1;// 没有排它锁,获取当前共享锁数量,并往共享锁的数量上 + 1int r = sharedCount(c);if (!readerShouldBlock() &&r < MAX_COUNT &&compareAndSetState(c, c + SHARED_UNIT)) {if (r == 0) {// 如果没有读锁,自己就是第一个读锁firstReader = current;firstReaderHoldCount = 1;} else if (firstReader == current) {// 读锁重入firstReaderHoldCount++;} else {// 如果第一个读线程不是自己,现在就要给总读线程数上+1了HoldCounter rh = cachedHoldCounter;if (rh == null ||rh.tid != LockSupport.getThreadId(current))cachedHoldCounter = rh = readHolds.get();else if (rh.count == 0)readHolds.set(rh);rh.count++;}// 此时就已经获取到了共享读锁return 1;}return fullTryAcquireShared(current);
}private void setHeadAndPropagate(Node node, int propagate) {// 把自己设置为头结点,然后查看自己后边的节点是否是共享模式Node h = head; // Record old head for check belowsetHead(node);if (propagate > 0 || h == null || h.waitStatus < 0 ||(h = head) == null || h.waitStatus < 0) {Node s = node.next;if (s == null || s.isShared())// 如果是共享模式,unpark后边的线程doReleaseShared();}
}private void doReleaseShared() {for (;;) {//唤醒操作由头结点开始,注意这里的头节点已经是上面新设置的头结点了//其实就是唤醒上面新获取到共享锁的节点的后继节点Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;//表示后继节点需要被唤醒if (ws == Node.SIGNAL) {//这里需要控制并发,因为入口有setHeadAndPropagate跟releaseShared两个,避免两次unparkif (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;      //执行唤醒操作      unparkSuccessor(h);}//如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                }//如果头结点没有发生变化,表示设置完成,退出循环//如果头结点发生变化,比如说其他线程获取到了锁,为了使自己的唤醒动作可以传递,必须进行重试if (h == head)                   break;
}

获取state的值,查看有无排它锁

有排它锁
  • 自旋等待
  • 创建一个共享模式节点用CAS操作添加到等待队列末尾
  • 调用LockSupport.park()阻塞线程
无排它锁
  • 共享锁数量+1,继续执行代码(或者直接跳出刚刚那个判断语句,直接开始执行代码)

4.2 readLock.unlock()

  • 普通读线程执行的之后,把state数量减-1

  • 最后一个读线程退出的时候,激活等待队列中的线程(此时队列中第一个肯定是写线程

4.3 writeLock.lock()

没有读线程 + 无读线程

setExclusiveOwnerThread为自己

有其他读线程

把自己加入到等待队列中,并且锁模式设置为排它锁

4.4 writeLock.unlock()

setExclusiveOwnerThreadnull,更新state数值

3. CountDownLatchAQS

state为门栓,当门栓为0时,表示线程不用被阻塞可以继续运行。

3.1 await()

  • 获取state是否为0,为0则可以直接继续执行,不用阻塞当前线程

  • 如果获取到当前门栓值大于0,阻塞当前线程

    • 创建一个共享锁模式的Node,用CAS操作添加到等待队列队尾

    • 调用LockSupport.park()阻塞线程

    • 更新等待队列的头结点为刚刚创建的节点,并把头结点的waitStatus设置为propgate

3.2 countDown()

  • CAS操作更新state的值,因为值大于0导致其他线程阻塞,所以在每次调用 state--

  • 当最后把state更新为0的时候,unparkSuccessor,解除头结点封印,又因为各个线程都是共享锁,所以一旦头结点执行,所有的等待队列中的线程均可以激活,周而复始的把自己设置为头结点,然后挤出原有的头结点

5. Varhandle

  • 普通属性进行原子操作
  • 比反射快,直接操纵二进制码

更多推荐

AQS ReentrantLock ReentrantReadWriteLock CountDownLatch源码阅读

本文发布于:2024-02-06 07:38:28,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1747461.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:源码   ReentrantLock   AQS   CountDownLatch   ReentrantReadWriteLock

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!