CountDownLatch
就是JUC包下的一个工具,整个工具最核心的功能就是计数器
如果有三个业务需要并行处理,并且需要知道三个业务全部都处理完毕了
需要一个并发安全的计数器来操作
CountDownLatch
就可以实现
给CountDownLatch
设置一个数值。可以设置3
每个业务处理完毕之后,执行一次countDown
方法,指定的3每次在执行countDown
方法时,对3
进行-1
主线程可以在业务处理时,执行await
,主线程会阻塞等待任务处理完毕
当设置的3
基于countDown
方法减为0
之后,主线程就会被唤醒,继续处理后续业务
当咱们的业务中,出现2个以上允许并行处理的任务,并且需要在任务都处理完毕后,再做其他处理时,可以采用CountDownLatch
去实现这个功能
模拟有三个任务需要并行处理,在三个任务全部处理完毕后,再执行后续操作
CountDownLatch
中,执行countDown
方法,代表一个任务结束,对计数器-1
执行await
方法,代表等待计数器变为0
时,再继续执行
执行await(time,unit)
方法,代表等待time
时长,如果计数器不为0
,返回false
,如果在等待期间,计数器为0
,方法就返回true
一般CountDownLatch
更多的是基于业务去构建,不采用成员变量
static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
static CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException {
System.out.println("主业务开始执行");
sleep(1000);
executor.execute(CompanyTest::a);
executor.execute(CompanyTest::b);
executor.execute(CompanyTest::c);
System.out.println("三个任务并行执行,主业务线程等待");
// 死等任务结束
// countDownLatch.await();
// 如果在规定时间内,任务没有结束,返回false
if (countDownLatch.await(10, TimeUnit.SECONDS)) {
System.out.println("三个任务处理完毕,主业务线程继续执行");
}else{
System.out.println("三个任务没有全部处理完毕,执行其他的操作");
}
}
private static void a() {
System.out.println("A任务开始");
sleep(1000);
System.out.println("A任务结束");
countDownLatch.countDown();
}
private static void b() {
System.out.println("B任务开始");
sleep(1500);
System.out.println("B任务结束");
countDownLatch.countDown();
}
private static void c() {
System.out.println("C任务开始");
sleep(2000);
System.out.println("C任务结束");
countDownLatch.countDown();
}
private static void sleep(long timeout){
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
保证CountDownLatch
就是一个计数器,没有什么特殊的功能,查看源码也只是查看计数器实现的方式
发现CountDownLatch
的内部类Sync
继承了AQS,CountDownLatch
就是基于AQS实现的计数器
AQS就是一个state
属性,以及AQS双向链表
猜测计数器的数值实现就是基于state
去玩的
主线程阻塞的方式,也是阻塞在了AQS双向链表中
就是构建内部类Sync
,并且给AQS中的state
赋值
// CountDownLatch的有参构造
public CountDownLatch(int count) {
// 健壮性校验
if (count < 0) throw new IllegalArgumentException("count < 0");
// 构建内部类,Sync传入count
this.sync = new Sync(count);
}
// AQS子类,Sync的有参构造
Sync(int count) {
// 就是给AQS中的state赋值
setState(count);
}
await
方法就时判断当前CountDownLatch
中的state
是否为0
,如果为0
,直接正常执行后续任务
如果不为0
,以共享锁的方式,插入到AQS的双向链表,并且挂起线程
// 一般主线程await的方法,阻塞主线程,等待state为0
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 执行了AQS的acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
// 判断线程是否中断,如果中断标记位是true,直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
// 共享锁挂起的操作
doAcquireSharedInterruptibly(arg);
}
// tryAcquireShared在CountDownLatch中的实现
protected int tryAcquireShared(int acquires) {
// 查看state是否为0,如果为0,返回1,不为0,返回-1
return (getState() == 0) ? 1 : -1;
}
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
// 封装当前先成为Node,属性为共享锁
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
// 在这,就需要挂起当前线程。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
countDown
方法本质就是对state - 1
,如果state - 1
后变为0
,需要去AQS的链表中唤醒挂起的节点
// countDown对计数器-1
public void countDown() {
// 是-1。
sync.releaseShared(1);
}
// AQS提供的功能
public final boolean releaseShared(int arg) {
// 对state - 1
if (tryReleaseShared(arg)) {
// state - 1后,变为0,执行doReleaseShared
doReleaseShared();
return true;
}
return false;
}
// CountDownLatch的tryReleaseShared实现
protected boolean tryReleaseShared(int releases) {
// 死循环是为了避免CAS并发问题
for (;;) {
// 获取state
int c = getState();
// state已经为0,直接返回false
if (c == 0)
return false;
// 对获取到的state - 1
int nextc = c-1;
// 基于CAS的方式,将值赋值给state
if (compareAndSetState(c, nextc))
// 赋值完,发现state为0了。此时可能会有线程在await方法处挂起,那边挂起,需要这边唤醒
return nextc == 0;
}
}
// 如何唤醒在await方法处挂起的线程
private void doReleaseShared() {
// 死循环
for (;;) {
// 拿到head
Node h = head;
// head不为null,有值,并且head != tail,代表至少2个节点
// 一个虚拟的head,加上一个实质性的Node
if (h != null && h != tail) {
// 说明AQS队列中有节点
int ws = h.waitStatus;
// 如果head节点的状态为 -1.
if (ws == Node.SIGNAL) {
// 先对head节点将状态从-1,修改为0,避免重复唤醒的情况
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 正常唤醒节点即可,先看head.next,能唤醒就唤醒,如果head.next有问题,从后往前找有效节点
unparkSuccessor(h);
}
// 会在Semaphore中谈到这个位置
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
// 会在Semaphore中谈到这个位置
if (h == head)
break;
}
}
从名字上来看CyclicBarrier
,就是代表循环屏障
Barrier
屏障:让一个或多个线程达到一个屏障点,会被阻塞。屏障点会有一个数值,当达到一个线程阻塞在屏障点时,就会对屏障点的数值进行-1
操作,当屏障点数值减为0
时,屏障就会打开,唤醒所有阻塞在屏障点的线程。在释放屏障点之后,可以先执行一个任务,再让所有阻塞被唤醒的线程继续之后后续任务
Cyclic
循环:所有线程被释放后,屏障点的数值可以再次被重置
CyclicBarrier
一般被称为栅栏
CyclicBarrier
是一种同步机制,允许一组线程互相等待。现成的达到屏障点其实是基于await方法在屏障点阻塞
CyclicBarrier
并没有基于AQS实现,他是基于ReentrantLock
锁的机制去实现了对屏障点--
,以及线程挂起的操作(CountDownLatch
本身是基于AQS,对state
进行release
操作后,可以-1
)
CyclicBarrier
没来一个线程执行await
,都会对屏障数值进行-1
操作,每次-1
后,立即查看数值是否为0
,如果为0
,直接唤醒所有的互相等待线程
CyclicBarrier对比CountDownLatch区别
CyclicBarrier
基于ReentrantLock
做的。CountDownLatch
直接基于AQS做的CountDownLatch
的计数器只能使用一次。而CyclicBarrier
在计数器达到0
之后,可以重置计数器。CyclicBarrier
可以实现相比CountDownLatch
更复杂的业务,执行业务时出现了错误,可以重置CyclicBarrier
计数器,再次执行一次CyclicBarrier
还提供了很多其他的功能:
CountDownLatch
一般是让主线程等待,让子线程对计数器--
。CyclicBarrier
更多的让子线程也一起计数和等待,等待的线程达到数值后,再统一唤醒CyclicBarrier
:多个线程互相等待,直到到达同一个同步点,再一次执行
出国旅游
导游小姐姐需要等待所有乘客都到位后,发送护照,签证等等文件,再一起出发
比如Tom,Jack,Rose三个人组个团出门旅游
在构建CyclicBarrier
可以指定barrierAction
,可以选择性指定,如果指定了,那么会在barrier
归0
后,优先执行barrierAction
任务,然后再去唤醒所有阻塞挂起的线程,并行去处理后续任务
所有互相等待的线程,可以指定等待时间,并且在等待的过程中,如果有线程中断,所有互相的等待的线程都会被唤醒
public static void main(String[] args) throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("等到各位大佬都到位之后,分发护照和签证等内容!");
});
new Thread(() -> {
System.out.println("Tom到位!!!");
try {
barrier.await();
} catch (Exception e) {
System.out.println("悲剧,人没到齐!");
return;
}
System.out.println("Tom出发!!!");
}).start();
Thread.sleep(100);
new Thread(() -> {
System.out.println("Jack到位!!!");
try {
barrier.await();
} catch (Exception e) {
System.out.println("悲剧,人没到齐!");
return;
}
System.out.println("Jack出发!!!");
}).start();
Thread.sleep(100);
new Thread(() -> {
System.out.println("Rose到位!!!");
try {
barrier.await();
} catch (Exception e) {
System.out.println("悲剧,人没到齐!");
return;
}
System.out.println("Rose出发!!!");
}).start();
}
console
Tom到位!!!
Jack到位!!!
Rose到位!!!
等到各位大佬都到位之后,分发护照和签证等内容!
Rose出发!!!
Tom出发!!!
Jack出发!!!
Process finished with exit code 0
CyclicBarrier的几种使用情况
- 如果在等待期间,有线程中断了,唤醒所有线程后,
CyclicBarrier
无法继续使用,线程中断后,需要继续使用当前的CyclicBarrier
,需要调用reset
方法,让CyclicBarrier
重置- 如果
CyclicBarrier
的屏障数值到达0
之后,他默认会重置屏障数值,CyclicBarrier
在没有线程中断时,是可以重复使用的- 如果某线程任务
await
设置等待时间超过等待时间程序还未执行,则所有调用CyclicBarrier
对象的任务都会终止,然后执行catch
里面的业务,且需要继续使用当前的CyclicBarrier
,需要调用reset
方法,让CyclicBarrier
重置
分成两块内容去查看,首先查看CyclicBarrier
的一些核心属性,然后再查看CyclicBarrier
的核心方法
public class CyclicBarrier {
// 这个静态内部类是用来标记是否中断的
private static class Generation {
boolean broken = false;
}
/** CyclicBarrier是基于ReentrantLock实现的互斥操作,以及计数原子性操作 */
private final ReentrantLock lock = new ReentrantLock();
/** 基于当前的Condition实现线程的挂起和唤醒 */
private final Condition trip = lock.newCondition();
/** 记录有参构造传入的屏障数值,不会对这个数值做操作 */
private final int parties;
/** 当屏障数值达到0之后,优先执行当前任务 */
private final Runnable barrierCommand;
/** 初始化默认的Generation,用来标记线程中断情况 */
private Generation generation = new Generation();
/** 每来一个线程等待,就对count进行-- */
private int count;
}
掌握构建CyclicBarrier
之后,内部属性的情况
// 这个是CyclicBarrier的有参构造
// 在内部传入了parties,屏障点的数值
// 还传入了barrierAction,屏障点的数值达到0,优先执行barrierAction任务
public CyclicBarrier(int parties, Runnable barrierAction) {
// 健壮性判
if (parties <= 0) throw new IllegalArgumentException();
// 当前类中的属性parties是保存屏障点数值的
this.parties = parties;
// 将parties赋值给属性count,每来一个线程,基于count做-1操作。
this.count = parties;
// 优先执行的任务
this.barrierCommand = barrierAction;
}
在CyclicBarrier
中,提供了2个await
方法
0
,或者有线程中断0
,或者有线程中断无论是哪种await
方法,核心都在于内部调用的dowait
方法
dowait
方法主要包含了线程互相等待的逻辑,以及屏障点数值到达0
之后的操作
// 包含了线程互相等到的逻辑,以及屏障点数值到达0后的操作
private int dowait(boolean timed, long nanos)throws
// 当前新编程中断,抛出这个异常
InterruptedException,
// 其他线程中断,当前线程抛出这个异常
BrokenBarrierException,
// await时间到位,抛出这个异常
TimeoutException {
// 加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 拿到Generation对象的引用
final Generation g = generation;
// 判断下线程中断了么?如果中断了,直接抛出异常
if (g.broken)
throw new BrokenBarrierException();
// 当前线程中断了么?
if (Thread.interrupted()) {
// 做了三个实现,
// 设置broken为true,将count重置,唤醒其他等待的线程
breakBarrier();
// 抛出异常
throw new InterruptedException();
}
// 屏障点做--
int index = --count;
// 如果屏障点为0,打开屏障啦!!
if (index == 0) {
// 标记
boolean ranAction = false;
try {
// 拿到有参构造中传递的任务
final Runnable command = barrierCommand;
// 任务不为null,优先执行当前任务
if (command != null)
command.run();
// 上述任务执行没问题,标记位设置为true
ranAction = true;
// 执行nextGeneration
// 唤醒所有线程,重置count,重置generation
nextGeneration();
return 0;
} finally {
// 如果优先执行的任务出了问题i,就直接抛出异常
if (!ranAction)
breakBarrier();
}
}
// 死循环
for (;;) {
try {
// 如果调用await方法,死等
if (!timed)
trip.await();
// 如果调用await(time,unit),基于设置的nans时长决定await的时长
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
// 到这,说明线程被中断了
// 查看generation有没有被重置。
// 并且当前broken为false,需要做线程中断后的操作。
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
Thread.currentThread().interrupt();
}
}
// 是否是中断唤醒,是就抛异常。
if (g.broken)
throw new BrokenBarrierException();
// 说明被reset了,返回index的数值。或者任务完毕也会被重置
if (g != generation)
return index;
// 指定了等待的时间内,没有等到所有线程都到位
if (timed && nanos <= 0L) {
// 中断任务
breakBarrier();
// 抛出异常
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
Synchronized
,ReentrantLock
是互斥锁,保证一个资源同一时间只允许被一个线程访问
Semaphore
(信号量)保证1个或多个资源可以被指定数量的线程同时访问
底层实现是基于AQS去做的
Semaphore
底层也是基于AQS的state
属性做一个计数器的维护。state
的值就代表当前共享资源的个数。如果一个线程需要获取的1个或多个资源,直接查看state
的标识的资源个数是否足够,如果足够的,直接对state - 1
拿到当前资源。如果资源不够,当前线程就需要挂起等待。知道持有资源的线程释放资源后,会归还给Semaphore
中的state
属性,挂起的线程就可以被唤醒
Semaphore
也分为公平和非公平的概念
使用场景:连接池对象就可以基础信号量去实现管理。在一些流量控制上,也可以采用信号量去实现。再比如去迪士尼或者是环球影城,每天接受的人流量是固定的,指定一个具体的人流量,可能接受10000人,每有一个人购票后,就对信号量进行--
操作,如果信号量已经达到了0,或者是资源不足,此时就不能买票
以上面环球影城每日人流量为例子去测试一下
public static void main(String[] args) throws InterruptedException {
// 今天环球影城还有人个人流量
Semaphore semaphore = new Semaphore(10);
new Thread(() -> {
try {
System.out.println("一家三口要去~~");
semaphore.acquire(3);
System.out.println("一家三口进去了~~~");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(3);
System.out.println("一家三口走了~~~");
}
}).start();
for (int i = 0; i < 7; i++) {
int j = i;
new Thread(() -> {
try {
System.out.println(j + "大哥来了。");
semaphore.acquire();
System.out.println(j + "大哥进去了~~~");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(j + "大哥走了~~~");
}
}).start();
}
Thread.sleep(10);
System.out.println("main大哥来了。");
if (semaphore.tryAcquire(10, TimeUnit.SECONDS)) {
System.out.println("main大哥进来了。");
} else {
System.out.println("资源不够,main大哥没进来。");
}
Thread.sleep(10000);
System.out.println("main大哥又来了。");
if (semaphore.tryAcquire()) {
System.out.println("main大哥进来了。");
semaphore.release();
} else {
System.out.println("资源不够,main大哥没进来。");
}
}
其实Semaphore
整体就是对构建Semaphore
时,指定的资源数的获取和释放操作
获取资源方式:
false
,有资源返回true
false
,有资源返回true
time.unit
,如果还没有,就返回false
time.unit
,如果还没有,就返回false
归还资源方式:
先查看Semaphore
的整体结构,然后基于获取资源,以及归还资源的方式去查看源码
Semaphore
内部有3个静态内类
首先是向上抽取的Sync
其次还有两个Sync
的子类NonFairSync
以及FairSync
两个静态内部类
Sync
内部主要提供了一些公共的方法,并且将有参构造传入的资源个数,直接基于AQS提供的setState
方法设置了state属性。
NonFairSync
以及FairSync
区别就是tryAcquireShared
方法的实现是不一样
在构建Semaphore
的时候,如果只设置资源个数,默认情况下是非公平
如果在构建Semaphore
,传入了资源个数以及一个boolean
时,可以选择非公平还是公平
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
从非公平的acquire
方法入手
首先确认默认获取资源数是1个,并且acquire
是允许中断线程时,抛出异常的。获取资源的方式,就是直接用state
-
需要的资源数,只要资源足够,就CAS的将state
做修改。如果没有拿到锁资源,就基于共享锁的方式去将当前线程挂起在AQS双向链表中。如果基于doAcquireSharedInterruptibly
拿锁成功,会做一个事情。会执行setHeadAndPropagate方法。一会说
// 信号量的获取资源方法(默认获取一个资源)
public void acquire() throws InterruptedException {
// 跳转到了AQS中提供共享锁的方法
sync.acquireSharedInterruptibly(1);
}
// AQS提供的
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
// 判断线程的中断标记位,如果已经中断,直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 先看非公平的tryAcquireShared实现。
// tryAcquireShared:
// 返回小于0,代表获取资源失败,需要排队。
// 返回大于等于0,代表获取资源成功,直接执行业务代码
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 信号量的非公平获取资源方法
final int nonfairTryAcquireShared(int acquires) {
// 死循环。
for (;;) {
// 获取state的数值,剩余的资源个数
int available = getState();
// 剩余的资源个数 - 需要的资源个数
int remaining = available - acquires;
// 如果-完后,资源个数小于0,直接返回这个负数
if (remaining < 0 ||
// 说明资源足够,基于CAS的方式,将state从原值,改为remaining
compareAndSetState(available, remaining))
return remaining;
}
}
// 获取资源失败,资源不够,当前线程需要挂起等待
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
// 构建Node节点,线程和共享锁标记,并且到AQS双向链表中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
// 拿到上一个节点
final Node p = node.predecessor();
// 如果上一个节点是头节点,就抢一手
if (p == head) {
// 再次基于非公平的方式去获取一次资源
int r = tryAcquireShared(arg);
// 到这,说明拿到了锁资源
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
// 如果上面没拿到,或者不是head的next节点,将前继节点的状态改为-1,并挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
// 如果线程中断会抛出异常
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
acquire()
以及acquire(int)
的方式,都是执行acquireSharedInterruptibly
方法去尝试获取资源,区别只在于是否传入了需要获取的资源个数
tryAcquire()
以及tryAcquire(int)
因为这两种方法是直接执行tryAcquire
,只使用非公平的实现,只有非公平的情况下,才有可能在有线程排队的时候获取到资源
但是tryAcquire(int,time,unit)
这种方法是正常走的AQS提供的acquire
。因为这个tryAcquire
可以排队一会,即便是公平锁也有可能拿到资源。这里的挂起和acquire
挂起的区别仅仅是挂起的时间问题
acquire
是一直挂起直到线程中断,或者线程被唤醒tryAcquire(int,time,unit)
是挂起一段时间,直到线程中断,要么线程被唤醒,要么阻塞时间到了还有acquireUninterruptibly()
以及acquireUninterruptibly(int)
只是在挂起线程后,不会因为线程的中断而去抛出异常
公平与非公平只是差了一个方法的实现tryAcquireShared
实现
这个方法的实现中,如果是公平实现,需要先查看AQS中排队的情况
// 信号量公平实现
protected int tryAcquireShared(int acquires) {
// 死循环。
for (;;) {
// 公平实现在走下述逻辑前,先判断队列中排队的情况
// 如果没有排队的节点,直接不走if逻辑
// 如果有排队的节点,发现当前节点处在head.next位置,直接不走if逻辑
if (hasQueuedPredecessors())
return -1;
// 下面这套逻辑和公平实现是一模一样的。
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
因为信号量从头到尾都是共享锁的实现……
释放资源操作,不区分公平和非公平
// 信号量释放资源的方法入口
public void release() {
sync.releaseShared(1);
}
// 释放资源不分公平和非公平,都走AQS的releaseShared
public final boolean releaseShared(int arg) {
// 优先查看tryReleaseShared,这个方法是信号量自行实现的。
if (tryReleaseShared(arg)) {
// 只要释放资源成功,执行doReleaseShared,唤醒AQS中排队的线程,去竞争Semaphore的资源
doReleaseShared();
return true;
}
return false;
}
// 信号量实现的释放资源方法
protected final boolean tryReleaseShared(int releases) {
// 死循环
for (;;) {
// 拿到当前的state
int current = getState();
// 将state + 归还的资源个数,新的state要被设置为next
int next = current + releases;
// 如果归还后的资源个数,小于之前的资源数。
// 避免出现归还资源后,导致next为负数,需要做健壮性判断
if (next < current)
throw new Error("Maximum permit count exceeded");
// CAS操作,保证原子性,只会有一个线程成功的就之前的state修改为next
if (compareAndSetState(current, next))
return true;
}
}
为了更好的了解PROPAGATE
节点状态的意义,优先从JDK1.5去分析一下释放资源以及排队后获取资源的后置操作
首先查看4个线程获取信号量资源的情况
往下查看释放资源的过程会触发什么问题
首先t1释放资源,做了进一步处理
当线程3获取锁资源后,线程2再次释放资源,因为执行点问题,导致线程4无法被唤醒
====================================JDK1.5实============================================.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 该方法类似JDK1.8的doReleaseShared方法,但少了其中else if的信号量状态(PROPAGATE)的判断
unparkSuccessor(h);
return true;
}
return false;
}
private void setHeadAndPropagate(Node node, int propagate) {
setHead(node);
if (propagate > 0 && node.waitStatus != 0) {
Node s = node.next;
if (s == null || s.isShared())
unparkSuccessor(node);
}
}
====================================JDK1.8实============================================.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
// 拿到head节点
Node h = head;
// 判断AQS中有排队的Node节点
if (h != null && h != tail) {
// 拿到head节点的状态
int ws = h.waitStatus;
// 状态为-1
if (ws == Node.SIGNAL) {
// 将head节点的状态从-1,改为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 唤醒后继节点
unparkSuccessor(h);
}
// 发现head状态为0,将head状态从0改为-3,目的是为了往后面传播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 没有并发的时候。head节点没变化,正常完成释放排队的线程
if (h == head)
break;
}
}
private void setHeadAndPropagate(Node node, int propagate) {
// 拿到head
Node h = head;
// 将线程3的Node设置为新的head
setHead(node);
// 如果propagate 大于0,代表还有剩余资源,直接唤醒后续节点,如果不满足,也需要继续往后判断看下是否需要传播
// h == null:看成健壮性判断即可
// 之前的head节点状态为负数,说明并发情况下,可能还有资源,需要继续向后唤醒Node
// 如果当前新head节点的状态为负数,继续释放后续节点
if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {
// 唤醒当前节点的后继节点
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}