多线程001 - 主线程等待子线程结束

举报
看山 发表于 2021/09/28 22:20:03 2021/09/28
【摘要】 在很多时候,我们期望实现这么一种功能:在主线程中启动一些子线程,等待所有子线程执行结束后,主线程再继续执行。比如:老板分配任务,众多工人开始工作,等所有工人完成工作后,老板进行检查。 解决方法分析: 主线程通过join等待所有子线程完成后,继续执行;主线程知道子线程的数量、未完成子线程数量,主线程等待所有子线程完成后,才继续执行。 ...

在很多时候,我们期望实现这么一种功能:在主线程中启动一些子线程,等待所有子线程执行结束后,主线程再继续执行。比如:老板分配任务,众多工人开始工作,等所有工人完成工作后,老板进行检查。

解决方法分析:

  1. 主线程通过join等待所有子线程完成后,继续执行;
  2. 主线程知道子线程的数量、未完成子线程数量,主线程等待所有子线程完成后,才继续执行。

通过join实现

第一种方式,可以直接调用Java API中关于线程的join方法等待该线程终止,可以直接实现。

每个工人都是一个线程,其run方法是工作的实现,每个工人通过名字进行区分,具体代码如下:


  
  1. package howe.demo.thread.join;
  2. import java.util.Random;
  3. import java.util.concurrent.TimeUnit;
  4. /**
  5. * @author liuxinghao
  6. * @version 1.0 Created on 2014年9月12日
  7. */
  8. public class Worker extends Thread {
  9. private String workerName;
  10. public Worker(String workerName) {
  11. this.workerName = workerName;
  12. }
  13. /**
  14. * @see java.lang.Thread#run()
  15. */
  16. @Override
  17. public void run() {
  18. System.out.println(this.workerName + "正在干活...");
  19. try {
  20. TimeUnit.SECONDS.sleep(new Random().nextInt(10));
  21. } catch (InterruptedException e) {
  22. }
  23. System.out.println(this.workerName + "活干完了!");
  24. }
  25. public String getWorkerName() {
  26. return workerName;
  27. }
  28. }

老板招收工人,然后安排工人工作,之后等待工人工作完,检查工人的工作成果(资本家啊。。。),具体代码如下:


  
  1. package howe.demo.thread.join;
  2. import java.util.List;
  3. /**
  4. * @author liuxinghao
  5. * @version 1.0 Created on 2014年9月12日
  6. */
  7. public class Boss {
  8. private List<Worker> workers;
  9. public Boss(List<Worker> workers) {
  10. System.out.println("老板招收工人。");
  11. this.workers = workers;
  12. }
  13. public void work() {
  14. System.out.println("老板开始安排工人工作...");
  15. for (Worker worker : workers) {
  16. System.out.println("老板安排" + worker.getWorkerName() + "的工作");
  17. worker.start();
  18. }
  19. System.out.println("老板安排工作结束...");
  20. System.out.println("老板正在等所有的工人干完活......");
  21. for (Worker w : workers) {
  22. try {
  23. w.join();
  24. } catch (InterruptedException e) {
  25. }
  26. }
  27. System.out.println("工人活都干完了,老板开始检查了!");
  28. }
  29. }

现在写main方法进行测试:


  
  1. package howe.demo.thread.join;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * @author liuxinghao
  6. * @version 1.0 Created on 2014年9月12日
  7. */
  8. public class JoinDemo {
  9. public static void main(String[] args) {
  10. Worker w1 = new Worker("张三");
  11. Worker w2 = new Worker("李四");
  12. Worker w3 = new Worker("王五");
  13. List<Worker> workers = new ArrayList<Worker>();
  14. workers.add(w1);
  15. workers.add(w2);
  16. workers.add(w3);
  17. Boss boss = new Boss(workers);
  18. boss.work();
  19. System.out.println("main方法结束");
  20. }
  21. }

执行结果为:

老板招收工人。
老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
王五活干完了!
张三活干完了!
李四活干完了!
工人活都干完了,老板开始检查了!
main方法结束

通过计数器实现

第二种方式可以自己实现一种计数器,用于统计子线程总数、未完成线程数,当未完成线程数大约0,主线程等待;当未完成线程数等于0,主线程继续执行。

当然,既然我们现在想到这种方式,Java API的团队当然也会想到,JDK 1.5提供了CountDownLatch用于实现上述方法。

于是对上述的工人方法进行修改:


  
  1. package howe.demo.thread.cdl;
  2. import java.util.Random;
  3. import java.util.concurrent.CountDownLatch;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * @author liuxinghao
  7. * @version 1.0 Created on 2014-9-12
  8. */
  9. public class Worker extends Thread {
  10. private CountDownLatch downLatch;
  11. private String workerName;
  12. public Worker(CountDownLatch downLatch, String workerName) {
  13. this.downLatch = downLatch;
  14. this.workerName = workerName;
  15. }
  16. public void run() {
  17. System.out.println(this.workerName + "正在干活...");
  18. try {
  19. TimeUnit.SECONDS.sleep(new Random().nextInt(10));
  20. } catch (InterruptedException ie) {
  21. }
  22. System.out.println(this.workerName + "活干完了!");
  23. this.downLatch.countDown();
  24. }
  25. public String getWorkerName() {
  26. return workerName;
  27. }
  28. }

latch.countDown(),是用于在子线程执行结束后计数器减一,即未完成子线程数减一。

老板类也得做出相应的修改:


  
  1. package howe.demo.thread.cdl;
  2. import java.util.List;
  3. import java.util.concurrent.CountDownLatch;
  4. /**
  5. * @author liuxinghao
  6. * @version 1.0 Created on 2014-9-12
  7. */
  8. public class Boss {
  9. private List<Worker> workers;
  10. private CountDownLatch downLatch;
  11. public Boss(List<Worker> workers, CountDownLatch downLatch) {
  12. this.workers = workers;
  13. this.downLatch = downLatch;
  14. }
  15. public void work() {
  16. System.out.println("老板开始安排工人工作...");
  17. for (Worker worker : workers) {
  18. System.out.println("老板安排" + worker.getWorkerName() + "的工作");
  19. worker.start();
  20. }
  21. System.out.println("老板安排工作结束...");
  22. System.out.println("老板正在等所有的工人干完活......");
  23. try {
  24. this.downLatch.await();
  25. } catch (InterruptedException e) {
  26. }
  27. System.out.println("工人活都干完了,老板开始检查了!");
  28. }
  29. }

latch.await(),是等待子线程结束。

编写main方法进行验证:


  
  1. package howe.demo.thread.cdl;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.CountDownLatch;
  5. /**
  6. * @author liuxinghao
  7. * @version 1.0 Created on 2014年9月15日
  8. */
  9. public class CountDownLatchDemo {
  10. public static void main(String[] args) {
  11. CountDownLatch latch = new CountDownLatch(3);
  12. Worker w1 = new Worker(latch, "张三");
  13. Worker w2 = new Worker(latch, "李四");
  14. Worker w3 = new Worker(latch, "王五");
  15. List<Worker> workers = new ArrayList<Worker>();
  16. workers.add(w1);
  17. workers.add(w2);
  18. workers.add(w3);
  19. Boss boss = new Boss(workers, latch);
  20. boss.work();
  21. System.out.println("main方法结束");
  22. }
  23. }

执行结果为:

老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
王五活干完了!
李四活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!
main方法结束

使用循环栅栏CyclicBarrier实现

还有一种实现,这种方式不会阻塞主线程,但是会监听所有子线程结束。此处在上述的工人老板的场景中使用的话,代码如下:

工人类:


  
  1. package howe.demo.thread.cyclicbarrier;
  2. import java.util.Random;
  3. import java.util.concurrent.BrokenBarrierException;
  4. import java.util.concurrent.CyclicBarrier;
  5. import java.util.concurrent.TimeUnit;
  6. /**
  7. * @author liuxinghao
  8. * @version 1.0 Created on 2014年9月12日
  9. */
  10. public class Worker extends Thread {
  11. private String workerName;
  12. private CyclicBarrier barrier;
  13. public Worker(String workerName, CyclicBarrier barrier) {
  14. this.workerName = workerName;
  15. this.barrier = barrier;
  16. }
  17. @Override
  18. public void run() {
  19. System.out.println(this.workerName + "正在干活...");
  20. try {
  21. TimeUnit.SECONDS.sleep(new Random().nextInt(10));
  22. } catch (InterruptedException e) {
  23. }
  24. System.out.println(this.workerName + "活干完了!");
  25. try {
  26. barrier.await();
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } catch (BrokenBarrierException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. public String getWorkerName() {
  34. return workerName;
  35. }
  36. }

老板类:


  
  1. package howe.demo.thread.cyclicbarrier;
  2. import java.util.List;
  3. /**
  4. * @author liuxinghao
  5. * @version 1.0 Created on 2014-9-12
  6. */
  7. public class Boss {
  8. private List<Worker> workers;
  9. public Boss(List<Worker> workers) {
  10. this.workers = workers;
  11. }
  12. public void work() {
  13. System.out.println("老板开始安排工人工作...");
  14. for (Worker worker : workers) {
  15. System.out.println("老板安排" + worker.getWorkerName() + "的工作");
  16. worker.start();
  17. }
  18. System.out.println("老板安排工作结束...");
  19. System.out.println("老板正在等所有的工人干完活......");
  20. }
  21. }

main方法测试:


  
  1. package howe.demo.thread.cyclicbarrier;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.CyclicBarrier;
  5. /**
  6. * @author liuxinghao
  7. * @version 1.0 Created on 2014年9月15日
  8. */
  9. public class CyclicBarrierDemo {
  10. public static void main(String[] args) {
  11. CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
  12. @Override
  13. public void run() {
  14. System.out.println("工人活都干完了,老板开始检查了!");
  15. }
  16. });
  17. Worker w1 = new Worker("张三", barrier);
  18. Worker w2 = new Worker("李四", barrier);
  19. Worker w3 = new Worker("王五", barrier);
  20. List<Worker> workers = new ArrayList<Worker>();
  21. workers.add(w1);
  22. workers.add(w2);
  23. workers.add(w3);
  24. Boss boss = new Boss(workers);
  25. boss.work();
  26. System.out.println("main方法结束");
  27. }
  28. }

执行结果为:

老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
main方法结束
李四活干完了!
王五活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!

通过结果分析可以很清楚的看出,boss对象的work方法执行结束后,main方法即开始执行。(此处“老板正在等所有的工人干完活......”打印之后是“王五正在干活...”,然后才是“main方法结束”,是因为对于cpu的抢占,甚至有一定的概率是“main方法结束”会在最后打印。)

假设有这么一个功能,我们需要向数据库批量写入一些记录,然后记录这个操作使用的时间,但是我们又不想影响其他操作(即不想阻塞主线程),这个时候CyclicBarrier就派上用场了。

文章来源: kanshan.blog.csdn.net,作者:看山,版权归原作者所有,如需转载,请联系作者。

原文链接:kanshan.blog.csdn.net/article/details/39299263

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。