java高並發系列 - 第31天:獲取線程執行結果,這6種方法你都知道?


這是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方法實現的,此方法返回一個Futurefuture.get()會讓當前線程阻塞,直到Future關聯的任務執行完畢。

相關知識:

  1. JAVA線程池,這一篇就夠了
  2. JUC中的Executor框架詳解1
  3. 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. 第1天:必須知道的幾個概念
  2. 第2天:並發級別
  3. 第3天:有關並行的兩個重要定律
  4. 第4天:JMM相關的一些概念
  5. 第5天:深入理解進程和線程
  6. 第6天:線程的基本操作
  7. 第7天:volatile與Java內存模型
  8. 第8天:線程組
  9. 第9天:用戶線程和守護線程
  10. 第10天:線程安全和synchronized關鍵字
  11. 第11天:線程中斷的幾種方式
  12. 第12天JUC:ReentrantLock重入鎖
  13. 第13天:JUC中的Condition對象
  14. 第14天:JUC中的LockSupport工具類,必備技能
  15. 第15天:JUC中的Semaphore(信號量)
  16. 第16天:JUC中等待多線程完成的工具類CountDownLatch,必備技能
  17. 第17天:JUC中的循環柵欄CyclicBarrier的6種使用場景
  18. 第18天:JAVA線程池,這一篇就夠了
  19. 第19天:JUC中的Executor框架詳解1
  20. 第20天:JUC中的Executor框架詳解2
  21. 第21天:java中的CAS,你需要知道的東西
  22. 第22天:JUC底層工具類Unsafe,高手必須要了解
  23. 第23天:JUC中原子類,一篇就夠了
  24. 第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)
  25. 第25天:掌握JUC中的阻塞隊列
  26. 第26篇:學會使用JUC中常見的集合,常看看!
  27. 第27天:實戰篇,接口性能提升幾倍原來這么簡單
  28. 第28天:實戰篇,微服務日志的傷痛,一並幫你解決掉
  29. 第29天:高並發中常見的限流方式
  30. 第30天:JUC中工具類CompletableFuture,必備技能

阿里p7一起學並發,公眾號:路人甲java,每天獲取最新文章!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM