背景

在一些场景中,我们需要获取多份数据,而这些数据获取的先后顺序是无关的,我们只需要把数据收集齐,然后再对这些数据统一处理。

比如:执行两个任务 task1 和 task2,都执行完毕后,再把两个任务的结果相加。

例如执行 task1 需要 1s,执行 task2 需要 2s,顺序执行,那么总时间是 1s + 2s = 3s。而如果我们用异步的方式,task1 和 task2 同时执行,则能减少响应的时间。

但并行的方式则必须注意,需要等待所有任务执行完毕之后,才能计算最终的结果,因此这就涉及多线程之间的等待。

方案一:Future.get() 获取数据

先创建一个线程池,并使用 ExecutorService.submit() 方法提交两个 Callable 任务。

提交任务后,这俩任务将开始异步并行执行,并返回了 Future 类型的对象,代表一个未来能获取结果的对象。

当我们调用 Future 对象的 get() 方法时,如果提交的任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么 get() 会阻塞当前线程,直到所有任务都完成后,才能获得执行的结果。

注意:当 submit() 时,任务就已经开始执行,但不会阻塞线程,当 get() 时,如果还有未完成的任务,才会阻塞当前主线程的运行,直到所有任务都完成后,才能获得执行的结果。

void executorServiceExample() throws Exception {

long time1 = System.currentTimeMillis();

// 任务1

Callable task1 = () -> {

Thread.sleep(1000);

return 10;

};

// 任务2

Callable task2 = () -> {

Thread.sleep(2000);

return 20;

};

// 创建线程池

ExecutorService service = Executors.newFixedThreadPool(2);

// 提交任务2个任务

Future submit1 = service.submit(task1);

Future submit2 = service.submit(task2);

// 获取任务结果,由于任务还未执行完毕,因此下一行将阻塞当前线程,直到所有任务都执行完毕

Integer result1 = submit1.get();

Integer result2 = submit2.get();

System.out.println("总耗时:" + (System.currentTimeMillis() - time1)); // 总耗时:2012

System.out.println(result1 + result2); // 30

service.shutdown();

}

方案二:CountdownLatch

CountdownLatch 用来控制一个或者多个线程等待多个线程。

维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。

CountdownLatch 计数方法

void countdownLatchExample() throws InterruptedException {

long time = System.currentTimeMillis();

// 创建 CountDownLatch,计数为2

CountDownLatch countDownLatch = new CountDownLatch(2);

// 创建线程池

ExecutorService executorService = Executors.newCachedThreadPool(2);

// 任务1

Runnable task1 = () -> {

System.out.println("begin task 1");

Thread.sleep(1000); // 省略 try-catch

System.out.println("end task 1");

countDownLatch.countDown(); // 计数减1

};

// 任务2

Runnable task2 = () -> {

System.out.println("begin task 2");

Thread.sleep(2000); // 省略 try-catch

System.out.println("end task 2");

countDownLatch.countDown(); // 计数减1

};

// 并行执行任务

executorService.execute(task1);

executorService.execute(task2);

// 等待2个任务执行完毕,countDownLatch计数器为0,才往下执行

countDownLatch.await();

System.out.println("total time: " + (System.currentTimeMillis() - time));

executorService.shutdown();

}

方案三:CyclicBarrier

CyclicBarrier 用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。

CyclicBarrier 计数方法

CyclicBarrier 和 CountdownLatch 的一个区别是:

1、CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。

2、countDownLatch.countDown() 只会将计数减 1,不会阻塞当前线程,countDownLatch.await() 只会阻塞当前线程,不会减少计数;而cyclicBarrier.await() 既会阻塞当前线程,也会计数减1。

CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 当计数为0时回调该方法。

void cyclicBarrierExample() {

long time = System.currentTimeMillis();

// 创建CyclicBarrier,计数为2,当计数为0时会回调该方法

CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> {

System.out.println("all task end");

System.out.println("total time: " + (System.currentTimeMillis() - time));

});

// 创建线程池

ExecutorService executorService = Executors.newCachedThreadPool();

// 任务1

Runnable task1 = () -> {

System.out.println("begin task 1");

Thread.sleep(1000); // 省略 try-catch

System.out.println("end task 1");

cyclicBarrier.await(); // 计数减1并阻塞当前线程,省略了try-catch

};

// 任务2

Runnable task2 = () -> {

System.out.println("begin task 2");

Thread.sleep(2000); // 省略 try-catch

System.out.println("end task 2");

cyclicBarrier.await(); // 计数减1并阻塞当前线程,省略了try-catch

};

executorService.execute(task1);

executorService.execute(task2);

executorService.shutdown();

}

java 当前线程 等待_Java 多线程等待相关推荐

  1. JAVA:线程总结及多线程实现的两种方法

    JAVA:线程总结 目录 目录 JAVA:线程总结 JAVA:线程总结 01_多线程(多线程的引入)(了解) 02_多线程(多线程并行和并发的区别)(了解) 03_多线程(Java程序运行原理和JVM ...

  2. java 线程等待队列_Java多线程学习(五)——等待通知机制

    等待通知机制的实现 方法wait()的作用是使当前线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程放到"预执行队列",并在wait()所在的代码处停止执行 ...

  3. java线程入门_java多线程快速入门(一)

    1.什么是进程 比如:QQ.QQ游戏.eclipse都是进程,可以通过任务管理器查看进程 2.进程和线程区别 线程是进程的一部分,一个进程可以包含多个线程,一个线程只能属于一个进程 进程是所有线程的集 ...

  4. java 线程 组成_java多线程

    一:基本知识点 1.1线程与进程区别: 1.进程是资源分配的最小单位,线程是CPU调度的最小单位 2.一个进程由一个或多个线程组成 3.进程之间相互独立,每个进程都有独立的代码和数据空间,但同一进程下 ...

  5. java中让步的_java 多线程—— 线程让步

    java 多线程 目录: 概述 第1 部分 yield()介绍 yield()的作用是让步.它能让当前线程由"运行状态"进入到"就绪状态",从而让其它具有相同优 ...

  6. thread.sleep是让哪个线程休眠_java多线程必看:java线程的生命周期

    点击蓝字 关注我们 线程是一个动态执行的过程,它也有从创建到死亡的过程.线程的几种状态 在 Thread 类中,有一个枚举内部类: 上面的信息以图片表示如下: 第一张图: 第二张图:把等待.计时等待. ...

  7. java线程名_java多线程

    首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程. 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈 ...

  8. java 线程简介_java多线程介绍

    java多线程介绍 多线程的基本实现 进程指运行中的程序,每个进程都会分配一个内存空间,一个进程中存在多个线程,启动一个JAVA虚拟机,就是打开个一个进程,一个进程有多个线程,当多个线程同时进行,就叫 ...

  9. java线程基础_Java多线程基础

    前言 在我们工作和学习的过程中,Java线程我们或多或少的都会用到,但是在使用的过程上并不是很顺利,会遇到各种各样的坑,这里我通过讲解Thread类中的核心方法,以求重点掌握以下关键技术点: 线程的启 ...

最新文章

  1. BestCoder Round #65 B C D || HDU 5591 5592 5593
  2. mysql 没有mysql库_MySQL安装之后没有MySQL数据库的原因
  3. Hive的安装和配置
  4. oracle中偏移,怎么对相同的坐标点偏移?
  5. 关于面试宝典中的各个问题(一)
  6. nohup xxx 后台进程关闭,可以这样避免
  7. 输入输出Fibonacci数
  8. [POJ3177]Redundant Paths(双联通)
  9. 华为P40或将搭载鸿蒙,华为P40或将在明年3月发布,很有可能是首部搭载鸿蒙的手机...
  10. matlab 0到正无穷求和,1/k!k从0到无穷求和是多少
  11. 远程桌面管理工具RDCMan
  12. pytest+seleniumUI自动化框架设计
  13. 顺势腹式呼吸还是逆势
  14. 【C语言编程练习】华氏转换为摄氏
  15. Android渲染时间 太长,Android性能优化之渲染篇
  16. 有道身份证查询接口API
  17. R语言—90分钟从入门到精通
  18. 移动硬盘读不出来,无法识别的6种修复方法
  19. android 编程词典,基于Android的英文词典的实现方法
  20. 使用python爬虫爬取百度新闻,告诉你社会热点话题

热门文章

  1. PHP树结构的应用,实现树状结构的两种方法-PHP教程,PHP应用
  2. c++详解缺省参数,缺省参数简介以及实际开发使用。
  3. 国产操作系统UOS安装教程
  4. 离开汽车之家和新车评,那些我们喜欢的车评人会活的更好
  5. OpenGL初探:二维卡通人物交互设计
  6. 高级测试开发进阶知识详解
  7. 谈谈红楼梦(第1-5回)
  8. golang 使用negroni,实现server
  9. Java 添加、读取、删除PPT文档属性
  10. natapp 配置微信小程序开发需要的网络环境