java 并发多线程 : 主线程等待子线程结束的三种方式:join / CountDownLatch / CyclicBarrier
Posted Mars-xq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 并发多线程 : 主线程等待子线程结束的三种方式:join / CountDownLatch / CyclicBarrier相关的知识,希望对你有一定的参考价值。
参考
在主线程中启动一些子线程,等待所有子线程执行结束后,主线程再继续执行。
比如:老板分配任务,众多工人开始工作,等所有工人完成工作后,老板进行检查。
解决方法分析:
-
主线程通过join等待所有子线程完成后,继续执行;
-
主线程知道子线程的数量、未完成子线程数量,主线程等待所有子线程完成后,才继续执行。
一、join
直接调用Java API中关于线程的join方法等待该线程终止,可以直接实现。
package day2;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class JoinDemo {
public static void main(String[] args) {
Worker w1 = new Worker("张三");
Worker w2 = new Worker("李四");
Worker w3 = new Worker("王五");
List<Worker> workers = new ArrayList<>();
workers.add(w1);
workers.add(w2);
workers.add(w3);
Boss boss = new Boss(workers);
boss.work();
System.out.println("main方法结束");
}
public static class Boss {
private final List<Worker> workers;
public Boss(List<Worker> workers) {
System.out.println("老板招收工人。");
this.workers = workers;
}
public void work() {
System.out.println("老板开始安排工人工作...");
for (Worker worker : workers) {
System.out.println("老板安排" + worker.getWorkerName() + "的工作");
worker.start();
}
System.out.println("老板安排工作结束...");
System.out.println();
System.out.println("老板正在等所有的工人干完活......");
for (Worker w : workers) {
try {
w.join();
} catch (InterruptedException e) {
System.out.println("e = " + e.getMessage());
}
}
System.out.println("工人活都干完了,老板开始检查了!");
}
}
public static class Worker extends Thread {
private final String workerName;
public Worker(String workerName) {
this.workerName = workerName;
}
@Override
public void run() {
System.out.println(this.workerName + "正在干活...");
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
System.out.println("e = " + e.getMessage());
}
System.out.println(this.workerName + "活干完了!");
}
public String getWorkerName() {
return workerName;
}
}
}
//老板招收工人。
//老板开始安排工人工作...
//老板安排张三的工作
//老板安排李四的工作
//老板安排王五的工作
//老板安排工作结束...
//
//老板正在等所有的工人干完活......
//张三正在干活...
//李四正在干活...
//王五正在干活...
//王五活干完了!
//张三活干完了!
//李四活干完了!
//工人活都干完了,老板开始检查了!
//main方法结束
二、CountDownLatch : 倒计时锁
可以自己实现一种计数器,用于统计子线程总数、未完成线程数,当未完成线程数大约0,主线程等待;当未完成线程数等于0,主线程继续执行。
当然,既然我们现在想到这种方式,Java API的团队当然也会想到,JDK 1.5提供了CountDownLatch用于实现上述方法。
latch 英 [lætʃ] 美 [lætʃ]
n. 门闩; 插销; 碰锁; 弹簧锁;
v. 用插销插上; 用碰锁锁上
package day2;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3);
Worker w1 = new Worker(latch, "张三");
Worker w2 = new Worker(latch, "李四");
Worker w3 = new Worker(latch, "王五");
List<Worker> workers = new ArrayList<Worker>();
workers.add(w1);
workers.add(w2);
workers.add(w3);
Boss boss = new Boss(workers, latch);
boss.work();
System.out.println("main方法结束");
}
static class Boss {
private final List<Worker> workers;
private final CountDownLatch downLatch;
public Boss(List<Worker> workers, CountDownLatch downLatch) {
this.workers = workers;
this.downLatch = downLatch;
}
public void work() {
System.out.println("老板开始安排工人工作...");
for (Worker worker : workers) {
System.out.println("老板安排" + worker.getWorkerName() + "的工作");
worker.start();
}
System.out.println("老板安排工作结束...");
System.out.println();
System.out.println("老板正在等所有的工人干完活......");
try {
//latch.await(),是等待子线程结束。
this.downLatch.await();
} catch (InterruptedException e) {
System.out.println("e = " + e.getMessage());
}
System.out.println("工人活都干完了,老板开始检查了!");
}
}
static class Worker extends Thread {
private final CountDownLatch downLatch;
private final String workerName;
public Worker(CountDownLatch downLatch, String workerName) {
this.downLatch = downLatch;
this.workerName = workerName;
}
public void run() {
System.out.println(this.workerName + "正在干活...");
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
System.out.println("e = " + e.getMessage());
}
System.out.println(this.workerName + "活干完了!");
//latch.countDown(),是用于在子线程执行结束后计数器减一,即未完成子线程数减一。
this.downLatch.countDown();
}
public String getWorkerName() {
return workerName;
}
}
}
//老板开始安排工人工作...
//老板安排张三的工作
//老板安排李四的工作
//老板安排王五的工作
//老板安排工作结束...
//
//老板正在等所有的工人干完活......
//张三正在干活...
//王五正在干活...
//李四正在干活...
//王五活干完了!
//张三活干完了!
//李四活干完了!
//工人活都干完了,老板开始检查了!
//main方法结束
三、CyclicBarrier : 循环屏障
还有一种实现,这种方式不会阻塞主线程,但是会监听所有子线程结束。此处在上述的工人老板的场景中使用的话,代码如下:
cyclic 英 [ˈsaɪklɪk] 美 [ˈsaɪklɪk]
adj. 循环的; 周期的;
barrier 英 [ˈbæriə(r)] 美 [ˈbæriər]
n. 屏障; 障碍物; 障碍; 阻力; 关卡; 分界线; 隔阂;
[其他] 复数:barriers
package day2;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierDemo {
public static void main(String[] args) {
// parties : 障碍清除之前,必须调用{@link#await}的线程数
// barrierAction : 当障碍物清除 时要执行的命令
CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("工人活都干完了,老板开始检查了!");
}
});
Worker w1 = new Worker("张三", barrier);
Worker w2 = new Worker("李四", barrier);
Worker w3 = new Worker("王五", barrier);
List<Worker> workers = new ArrayList<Worker>();
workers.add(w1);
workers.add(w2);
workers.add(w3);
Boss boss = new Boss(workers);
boss.work();
System.out.println("main方法结束");
}
public static class Boss {
private final List<Worker> workers;
public Boss(List<Worker> workers) {
this.workers = workers;
}
public void work() {
System.out.println("老板开始安排工人工作...");
for (Worker worker : workers) {
System.out.println("老板安排" + worker.getWorkerName() + "的工作");
worker.start();
}
System.out.println("老板安排工作结束...");
System.out.println();
System.out.println("老板正在等所有的工人干完活......");
}
}
public static class Worker extends Thread {
private final String workerName;
private final CyclicBarrier barrier;
public Worker(String workerName, CyclicBarrier barrier) {
this.workerName = workerName;
this.barrier = barrier;
}
@Override
public void run() {
System.out.println(this.workerName + "正在干活...");
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
System.out.println("e = " + e.getMessage());
}
System.out.println(this.workerName + "活干完了!");
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
public String getWorkerName() {
return workerName;
}
}
}
//老板开始安排工人工作...
//老板安排张三的工作
//老板安排李四的工作
//老板安排王五的工作
//老板安排工作结束...
//
//老板正在等所有的工人干完活......
//main方法结束
//王五正在干活...
//张三正在干活...
//李四正在干活...
//李四活干完了!
//王五活干完了!
//张三活干完了!
//工人活都干完了,老板开始检查了!
以上是关于java 并发多线程 : 主线程等待子线程结束的三种方式:join / CountDownLatch / CyclicBarrier的主要内容,如果未能解决你的问题,请参考以下文章