这是 java 高并发系列第 31 篇.
环境: jdk1.8.
java 高并发系列已经学了不少东西了, 本篇文章, 我们用前面学的知识来实现一个需求:
在一个线程中需要获取其他线程的执行结果, 能想到几种方式? 各有什么优缺点?
结合这个需求, 我们使用 6 种方式, 来对之前学过的知识点做一个回顾, 加深记忆.
方式 1:Thread 的 join() 方法实现
代码:
- package com.itsoku.chat31;
- import java.sql.Time;
- import java.util.concurrent.*;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo1 {
- // 用于封装结果
- static class Result<T> {
- T result;
- public T getResult() {
- return result;
- }
- public void setResult(T result) {
- this.result = result;
- }
- }
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- System.out.println(System.currentTimeMillis());
- // 用于存放子线程执行的结果
- Result<Integer> result = new Result<>();
- // 创建一个子线程
- Thread thread = new Thread(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- result.setResult(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- });
- thread.start();
- // 让主线程等待 thread 线程执行完毕之后再继续, join 方法会让当前线程阻塞
- thread.join();
- // 获取 thread 线程的执行结果
- Integer rs = result.getResult();
- System.out.println(System.currentTimeMillis());
- System.out.println(System.currentTimeMillis() + ":" + rs);
- }
- }
输出:
- 1566733162636
- 1566733165692
- 1566733165692:10
代码中通过 join 方式阻塞了当前主线程, 当 thread 线程执行完毕之后, join 方法才会继续执行.
join 的方式, 只能阻塞一个线程, 如果其他线程中也需要获取 thread 线程的执行结果, join 方法无能为力了.
关于 join() 方法和线程更详细的使用, 可以参考: 线程的基本操作
方式 2:CountDownLatch 实现
代码:
- package com.itsoku.chat31;
- import java.util.concurrent.*;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo2 {
- // 用于封装结果
- static class Result<T> {
- T result;
- public T getResult() {
- return result;
- }
- public void setResult(T result) {
- this.result = result;
- }
- }
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- System.out.println(System.currentTimeMillis());
- CountDownLatch countDownLatch = new CountDownLatch(1);
- // 用于存放子线程执行的结果
- Demo1.Result<Integer> result = new Demo1.Result<>();
- // 创建一个子线程
- Thread thread = new Thread(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- result.setResult(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally {
- countDownLatch.countDown();
- }
- });
- thread.start();
- //countDownLatch.await() 会让当前线程阻塞, 当 countDownLatch 中的计数器变为 0 的时候, await 方法会返回
- countDownLatch.await();
- // 获取 thread 线程的执行结果
- Integer rs = result.getResult();
- System.out.println(System.currentTimeMillis());
- System.out.println(System.currentTimeMillis() + ":" + rs);
- }
- }
输出:
- 1566733720406
- 1566733723453
- 1566733723453:10
上面代码也达到了预期效果, 使用 CountDownLatch 可以让一个或者多个线程等待一批线程完成之后, 自己再继续; CountDownLatch 更详细的介绍见: JUC 中等待多线程完成的工具类 CountDownLatch, 必备技能
方式 3:ExecutorService.submit 方法实现
代码:
- package com.itsoku.chat31;
- import java.util.concurrent.*;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo3 {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- // 创建一个线程池
- ExecutorService executorService = Executors.newCachedThreadPool();
- System.out.println(System.currentTimeMillis());
- Future<Integer> future = executorService.submit(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return 10;
- });
- // 关闭线程池
- executorService.shutdown();
- System.out.println(System.currentTimeMillis());
- Integer result = future.get();
- System.out.println(System.currentTimeMillis() + ":" + result);
- }
- }
输出:
- 1566734119938
- 1566734119989
- 1566734122989:10
使用 ExecutorService.submit 方法实现的, 此方法返回一个 Future,future.get() 会让当前线程阻塞, 直到 Future 关联的任务执行完毕.
相关知识:
JAVA 线程池, 这一篇就够了
JUC 中的 Executor 框架详解 1
JUC 中的 Executor 框架详解 2
方式 4:FutureTask 方式 1
代码:
- package com.itsoku.chat31;
- import java.util.concurrent.*;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo4 {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- System.out.println(System.currentTimeMillis());
- // 创建一个 FutureTask
- FutureTask<Integer> futureTask = new FutureTask<>(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return 10;
- });
- // 将 futureTask 传递一个线程运行
- new Thread(futureTask).start();
- System.out.println(System.currentTimeMillis());
- //futureTask.get() 会阻塞当前线程, 直到 futureTask 执行完毕
- Integer result = futureTask.get();
- System.out.println(System.currentTimeMillis() + ":" + result);
- }
- }
输出:
- 1566736350314
- 1566736350358
- 1566736353360:10
代码中使用 FutureTask 实现的, FutureTask 实现了 Runnable 接口, 并且内部带返回值, 所以可以传递给 Thread 直接运行, futureTask.get() 会阻塞当前线程, 直到 FutureTask 构造方法传递的任务执行完毕, get 方法才会返回. 关于 FutureTask 详细使用, 请参考: JUC 中的 Executor 框架详解 1
方式 5:FutureTask 方式 2
代码:
- package com.itsoku.chat31;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- import java.util.concurrent.TimeUnit;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo5 {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- System.out.println(System.currentTimeMillis());
- // 创建一个 FutureTask
- FutureTask<Integer> futureTask = new FutureTask<>(() -> 10);
- // 将 futureTask 传递一个线程运行
- new Thread(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- futureTask.run();
- }).start();
- System.out.println(System.currentTimeMillis());
- //futureTask.get() 会阻塞当前线程, 直到 futureTask 执行完毕
- Integer result = futureTask.get();
- System.out.println(System.currentTimeMillis() + ":" + result);
- }
- }
输出:
- 1566736319925
- 1566736319970
- 1566736322972:10
创建了一个 FutureTask 对象, 调用 futureTask.get() 会阻塞当前线程, 子线程中休眠了 3 秒, 然后调用 futureTask.run(); 当 futureTask 的 run() 方法执行完毕之后, futureTask.get() 会从阻塞中返回.
注意: 这种方式和方式 4 的不同点.
关于 FutureTask 详细使用, 请参考: JUC 中的 Executor 框架详解 1
方式 6:CompletableFuture 方式实现
代码:
- package com.itsoku.chat31;
- import java.util.concurrent.CompletableFuture;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- import java.util.concurrent.TimeUnit;
- /**
- * 跟着阿里 p7 学并发, 微信公众号: javacode2018
- */
- public class Demo6 {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- System.out.println(System.currentTimeMillis());
- CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return 10;
- });
- System.out.println(System.currentTimeMillis());
- //futureTask.get() 会阻塞当前线程, 直到 futureTask 执行完毕
- Integer result = completableFuture.get();
- System.out.println(System.currentTimeMillis() + ":" + result);
- }
- }
输出:
- 1566736205348
- 1566736205428
- 1566736208429:10
CompletableFuture.supplyAsync 可以用来异步执行一个带返回值的任务, 调用 completableFuture.get()
会阻塞当前线程, 直到任务执行完毕, get 方法才会返回.
关于 CompletableFuture 更详细的使用见: JUC 中工具类 CompletableFuture, 必备技能
java 高并发系列目录
第 1 天: 必须知道的几个概念
第 2 天: 并发级别
第 3 天: 有关并行的两个重要定律
第 4 天: JMM 相关的一些概念
第 5 天: 深入理解进程和线程
第 6 天: 线程的基本操作
第 7 天: volatile 与 Java 内存模型
第 8 天: 线程组
第 9 天: 用户线程和守护线程
第 10 天: 线程安全和 synchronized 关键字
第 11 天: 线程中断的几种方式
第 12 天 JUC:ReentrantLock 重入锁
第 13 天: JUC 中的 Condition 对象
第 14 天: JUC 中的 LockSupport 工具类, 必备技能
第 15 天: JUC 中的 Semaphore(信号量)
第 16 天: JUC 中等待多线程完成的工具类 CountDownLatch, 必备技能
第 17 天: JUC 中的循环栅栏 CyclicBarrier 的 6 种使用场景
第 18 天: JAVA 线程池, 这一篇就够了
第 19 天: JUC 中的 Executor 框架详解 1
第 20 天: JUC 中的 Executor 框架详解 2
第 21 天: java 中的 CAS, 你需要知道的东西
第 22 天: JUC 底层工具类 Unsafe, 高手必须要了解
第 23 天: JUC 中原子类, 一篇就够了
第 24 天: ThreadLocal,InheritableThreadLocal(通俗易懂)
第 25 天: 掌握 JUC 中的阻塞队列
第 26 篇: 学会使用 JUC 中常见的集合, 常看看!
第 27 天: 实战篇, 接口性能提升几倍原来这么简单
第 28 天: 实战篇, 微服务日志的伤痛, 一并帮你解决掉
第 29 天: 高并发中常见的限流方式
第 30 天: JUC 中工具类 CompletableFuture, 必备技能
来源: http://www.bubuko.com/infodetail-3170830.html