输出结果:
这个t1和t2的输出结果是随机的。
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties,Runnable barrier- Action),用于在线程到达屏障时,优先执行barrierAction。
package com.zwx.concurrent.jucUtil;
import java.util.concurrent.CyclicBarrier;
public class CyclicbarrierDemo2 {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(2,new MyThread());
public static void main(String[] args) {
new Thread(()-> {
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(“我是线程t1”);
},“t1”).start();
new Thread(()-> {
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(“我是线程t2”);
},“t2”).start();
System.out.println(“主线程==end”);
}
static class MyThread extends Thread {
@Override
public void run() {
System.out.println(“do something”);
}
}
}
输出结果:
我们可以看到,在线程t1和t2输出前会先输出自定义线程的信息。
CyclicBarrier 相比 CountDownLatch 来说,要简单很多,源码实现是基于 ReentrantLock 和 Condition 的组合使用
CyclicBarrier(parties)
进入CyclicBarrier默认构造器:
可以发现,最终其实还是调用的CyclicBarrier(int parties,Runnable barrier- Action)构造器:
注意了,构造CyclicBarrier对象时,初始化了多少个parties,则必须对应有parties个线程调用await()方法,否则线程不会往后执行。
CyclicBarrier#await()
调用了dowait(timed,nanos)方法,第一个参数false表示未设置超时时间,后面表示纳秒数,因为await还有另一个对应的方法带上超时时间:await(long,timeunit),这个方法中调用dowait(timed,nanos)方法时第一个参数就会是true,然后带上超时时间,表示到了设定时间之后线程就不会被阻塞,会继续往后执行。
CyclicBarrier#dowait()
/**
-
Main barrier code, covering the various policies.
-
主要屏障代码,覆盖了各种策略
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;//定义一个重入锁:private final ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
//同一个屏障初始进来时属于同一代或者说一个周期,构建一个"代"(Generation)对象,同一个Generation表示同一代
final Generation g = generation;//Generation中设置了broke=false,表示屏障没有损坏
if (g.broken)//如果broken=true表示当前屏障被损坏了,抛出异常
throw new BrokenBarrierException();
if (Thread.interrupted()) {//如果线程被中断过
breakBarrier();//设置屏障为损坏状态并唤醒所有持有锁的线程
throw new InterruptedException();//抛出中断异常
}
int index = --count;//未调用await()方法的线程计数-1
if (index == 0) {//如果屏障数为0,(表示所有线程都到达await()方法)
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();//表示到达屏障之后,如果我们有设置barrierCommand,则优先执行
ranAction = true;
//执行到这里的时候,说明所有线程都到了await()方法,且设置的barrierCommand也已经执行完了
//接下来要做的事情就是换代(所以CyclicBarrier是通过换代的方式实现重新计数的)
//换代之后相当于进入一个新的周期,所有线程在后续中又可以通过await()阻塞一次
nextGeneration();
return 0;
} finally {
if (!ranAction)//如果ranAction = false说明当前屏障还有流程没执行完,所以需要屏障设置会损坏状态
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
//死循环等到count=0,调用breakBarrier方法(表示屏障有问题的场景),中断或者超时
for (;😉 {
try {
if (!timed)
//private final Condition trip = lock.newCondition();
trip.await();//即Condition队列的await()阻塞,相当于把线程加入到Condition队列中阻塞
else if (nanos > 0L)//超时时间大于0
nanos = trip.awaitNanos(nanos);//阻塞指定时间
} catch (InterruptedException ie) {
//如果当前屏障没有换代,也没有损坏,那么就设置为损坏状态之后再抛出中断异常
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We’re about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// “belong” to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)//如果屏障已经被损坏了
throw new BrokenBarrierException();
if (g != generation)//如果发现已经换代了,就不继续循环了,直接返回就好了
return index;//返回当前还有多少个线程没有执行await()方法
if (timed && nanos <= 0L) {//表示超时时间到了
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
这里的方法看起来很长,但其实除了一系列的判断,并没有多代码,结合注释,如果前面理解了ReentrantLock和Condition队列的话,应该非常好看懂,里面调用的其他一些子方法这里也不做单独介绍。
1、对于指定计数值 parties,若由于某种原因,没有足够的线程调用 CyclicBarrier 的await()方法,则所有调用 await 的线程都会被阻塞;
2、若有多余线程执行了await()方法,那么最后一个到达屏障的线程会被阻塞
3、通过 reset 重置计数,会使得进入 await 的线程出现BrokenBarrierException;我们可以通过捕获异常重新处理业务逻辑
4、如果采用是 CyclicBarrier(int parties, Runnable barrierAction) 构造方法,执行 barrierAction 操作的是最后一个到达的线程。
CountDownLatch和CyclicBarrier区别
===========================================================================================
- CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数 器,并让线程重新执行一次。
======================================================================
Semaphore也就是我们常说的信号灯,Semaphore可以控制同时访问的线程个数,通过 acquire 获取一个许可,如果没有就等待,通过 release 释放一个许可。有点类似限流
的作用。叫信号灯的原因也和他的用处有关,比如某商场就 5 个停车位,每个停车位只能停一辆车,如果这个时候来了 10 辆车,必须要等前面有空的车位才能进入。
package com.zwx.concurrent.jucUtil;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(2);
for(int i=1;i<=5;i++){
new Car(i,semaphore).start();
}
}
}
class Car extends Thread{
private int num;
private Semaphore semaphore;
public Car(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();//获取一个许可
System.out.println(“第”+num+“辆车进来了”);
TimeUnit.SECONDS.sleep(2);
System.out.println(“第”+num+“辆车出去了”);
semaphore.release();
} catch (InterruptedException e) {
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
在。**
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-8N1iYVIu-1710903103756)]
[外链图片转存中…(img-732PFnrI-1710903103757)]
[外链图片转存中…(img-5EGfAoL4-1710903103757)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-o7QenmyB-1710903103757)]