java高並發系列 – 第31天:獲取線程執行結果,這6種方法你都知道?
- 2019 年 10 月 3 日
- 筆記
這是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關聯的任務執行完畢。
相關知識:
方式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,必備技能
阿里p7一起學並發,公眾號:路人甲java,每天獲取最新文章!

