背景
这两JUC类适合放一起比较,随便写写,简单小结。
CountDownLatch
要点:
- 底层采用AQS队列,源码很简单300行。
使用:
- 初始化对象并填入(计数器)值,例如:new CountDownLatch(3)。多线程执行完毕时,触发countdown方法将计数器值减1,当值为0时,主线程中被Latch拴住的代码开始执行。
用法一:
public class CountDownLatch_v1 {
//计数器设为3
static CountDownLatch latch = new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(
() ->
{
latch.countDown();
System.out.println("T1 is OK !");
}
);
Thread t2 = new Thread(
() ->
{
System.out.println("T2 is OK !");
latch.countDown();
}
);
Thread t3 = new Thread(
() ->
{
System.out.println("T3 is OK !");
latch.countDown();
}
);
t1.start();
t2.start();
t3.start();
latch.await(); //main线程阻塞中,等待t1 t2 t3都countDown后,计数器从3变为0后继续运行
System.out.println("Every T is OK !");
}
}
用法二:
public class CountDownLatch_v2 {
static CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(
() ->
{
try {
latch.await();
System.out.println("T1 Beginning !");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
Thread t2 = new Thread(
() ->
{
try {
latch.await();
System.out.println("T2 Beginning !");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
Thread t3 = new Thread(
() ->
{
try {
latch.await();
System.out.println("T3 Beginning !");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
t1.start();
t2.start();
t3.start();
System.out.println("Start !");
latch.countDown();
}
}
用法场景:
- Main线程等待多个线程T执行完毕后才能执行。多个线程T执行各自的代码段,执行完在线程中执行CountDown方法(减1),减到0后触发Main线程执行。
- Main线程等待 T1,T2 两个线程 ,只有两个线程执行达到固定次数(线程内可反复CountDown)后Main线程才能执行。
- 为达到最大并行度,Main线程在某一时刻,启动多个线程T。
PS:new CountDownLatch(1);
多个T线程中 执行await(); //所有T线程阻塞等待。
Main线程中执行CountDown //主线程一声令下,多个T线程开始执行。 - 情况3和1的结合,多个线程T都干完后,同时开启多个线程M做下一件事。
不足:
- CountDownLatch 对象New出来后只能使用一次。无法重新赋值反复使用。
CyclicBarrier
要点:
- 源码不到500,方法不多,采用RE重入锁。
- 多个线程中通过调用barrier.await相互等待,达到触发点后,触发新线程中自定义方法执行。
- 精髓在于Cyclic,定义的触发方法可被反复多次触发(前提:CyclicBarrier没有Broken)。
使用:
- CyclicBarrier构造方法中,指定触发阈值、触发方法(新线程)。达到触发次数时,开启新线程执行触发方法。
- 子线程中调用barrier.await一次即可(写多次await调用会抛异常),然后就被绊倒了(被阻塞)。
用法一:CyclicBarrier循环多次触发。
public class CyclicBarrier_v2 {
static CyclicBarrier barrier ;
public static void main(String[] args) throws InterruptedException {
barrier = new CyclicBarrier(3,
() -> {
System.out.println("Every one is OK !");
});
Thread t1 = new Thread(
() ->
{
try {
for(int i=0; i <10; i++) {
System.out.println("T1 is OK !");
barrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
);
Thread t2 = new Thread(
() ->
{
try {
for(int i=0; i <10; i++) {
System.out.println("T2 is OK !");
barrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
);
Thread t3 = new Thread(
() ->
{
try {
for(int i=0; i <10; i++) {
System.out.println("T3 is OK !");
barrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
);
t1.start();
t2.start();
t3.start();
}
}
用法二:二线程交替执行。
public class CyclicBarrier_v3 {
static CyclicBarrier barrier ;
public static void main(String[] args) throws InterruptedException {
barrier = new CyclicBarrier(1,
() -> {
System.out.println("A is OK !");
});
Thread t1 = new Thread(
() ->
{
try {
for(int i=0; i <10; i++) {
System.out.println("T1 is OK !");
barrier.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
);
t1.start();
}
}
用法场景:
- 多个线程执行时需要相互等待,达到等待阈值时,触发新线程执行,可反复触发。
- 可实现2个线程交替执行。
注意:
- 当使用CyclicBarrier做等待(await)的线程被其他线程执行Interrupt方法打断时,会抛出2个异常,InterruptedException和BrokenBarrierException,使栅栏失效。
CountDownLatch 和 CyclicBarrier 区别
- CyclicBarrier不会阻塞主线程,CountDownLatch会。
- CyclicBarrier会阻塞子线程,CountDownLatch不会。
- 由于CountDownLatch的CountDown方法不阻塞,所以CountDownLatch用于等待事件发生(当某个事件发生了可运行CountDown方法,且可多次调用)。而CyclicBarrier用于本线程等待其他线程(所有线程await相互等待,达到阈值后,结束等待,触发新线程)。
- CountDownLatch只能用一次,再用就得重新new;CyclicBarrier可以循环使用。