摘要:使用獲得返回結果新建個線程,每個線程分別負責累加慢速累加器線程等待線程執行完畢累加的結果運行結束,結果為運行結果第二種方法使用和。
Java 對多線程編程提供了內置的支持并提供了良好的 API,通過使用 Thread 和 Runnable 兩個基礎類,我們可以很方便的創建一個線程:
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("線程啟動"); // 耗時操作 System.out.println("線程結束"); } }; Thread thread = new Thread(runnable); // 創建線程,runnable 作為線程要執行的任務(載體) thread.start(); // 啟動線程 thread.join(); // 等待線程執行完畢
{ 題外話開始:
通過 Thread 的類聲明:
我們可以知道 Thread 自己也實現了 Runnable 接口,Thread 中 run 方法的實現如下(Thread 啟動之后運行的就是 Thread 中的 run 方法):
(target 即構造 Thread 時可傳入的 Runnable 對象,不傳入即為 null —— 所以繼承 Thread 重寫其 run 方法也是一種創建線程的方式)
題外話結束 }
Runnable.java 的代碼:
Runnable 的 run 方法是不帶返回值的,那如果我們需要一個耗時任務在執行完之后給予返回值,應該怎么做呢?
第一種方法:在 Runnable 的實現類中設置一個變量 V,在 run 方法中將其改變為我們期待的結果,然后通過一個 getV() 方法將這個變量返回。
import java.util.*; public class RunnableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Runnable 獲得返回結果:"); Listworkers = new ArrayList<>(10); List tasks = new ArrayList<>(10); // 新建 10 個線程,每個線程分別負責累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumRunnable task = new AccumRunnable(i * 10 + 1, (i + 1) * 10); Thread worker = new Thread(task, "慢速累加器線程" + i); tasks.add(task); workers.add(worker); worker.start(); } int total = 0; for (int i = 0, s = workers.size(); i < s; i++) { workers.get(i).join(); // 等待線程執行完畢 total += tasks.get(i).getResult(); } System.out.println(" 累加的結果: " + total); } static final class AccumRunnable implements Runnable { private final int begin; private final int end; private int result; public AccumRunnable(int begin, int end) { this.begin = begin; this.end = end; } @Override public void run() { result = 0; try { for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } } catch (InterruptedException ex) { ex.printStackTrace(System.err); } System.out.printf("(%s) - 運行結束,結果為 %d ", Thread.currentThread().getName(), result); } public int getResult() { return result; } } }
運行結果:
第二種方法:使用 Callable
Callable
Callable.java 的代碼:
可以看到參數化類型 V 就是返回的值的類型。
但是查看 Thread 的構造方法,我們發現 Thread 只提供了將 Runnable 作為參數的構造方法,并沒有使用 Callable
FutureTask
可以看到它實現了 RunnableFuture
可以看到 RunnableFuture 接口繼承了 Runnable 接口,那么 RunnableFuture 接口的實現類 FutureTask 必然會去實現 Runnable 接口 —— 所以 FutureTask 可以用來當 Runnable 使用。查看 FutureTask 的構造方法,發現 FutureTask 有兩個構造方法:
第一個構造方法表明我們可以通過一個 Callable 去構造一個 FutureTask —— 而 FutureTask 實現了 Runnable 接口,從而可以將該任務傳遞給 Thread 去運行;
第二個構造方法是通過一個 Runnable 和一個指定的 result 去構造 FutureTask。
我們再來看看 FutureTask
(事實上,RunnableFuture
Future.java 的源碼:
Future
(Future 每個方法的詳細用法可以參考 Java 多線程(3))
通過以上我們可以知道,Callable
現在我們看看 FutureTask 中實現 Runnable 的 run 方法是怎樣的(我們直接看關鍵代碼):
代碼的意思很明確,調用構造 FutureTask
使用 FutureTask
(1)通過一個 Callable
(2)將 FutureTask
(3)使用 FutureTask
現在我們使用 Callable 改寫程序:
import java.util.*; import java.util.concurrent.*; public class CallableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Callable 獲得返回結果:"); List> futureTasks = new ArrayList<>(10); // 新建 10 個線程,每個線程分別負責累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumCallable task = new AccumCallable(i * 10 + 1, (i + 1) * 10); FutureTask futureTask = new FutureTask<>(task); futureTasks.add(futureTask); Thread worker = new Thread(futureTask, "慢速累加器線程" + i); worker.start(); } int total = 0; for (FutureTask futureTask : futureTasks) { total += futureTask.get(); // get() 方法會阻塞直到獲得結果 } System.out.println("累加的結果: " + total); } static final class AccumCallable implements Callable { private final int begin; private final int end; public AccumCallable(int begin, int end) { this.begin = begin; this.end = end; } @Override public Integer call() throws Exception { int result = 0; for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } System.out.printf("(%s) - 運行結束,結果為 %d ", Thread.currentThread().getName(), result); return result; } } }
運行結果:
可以看到使用 Callable
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66355.html
摘要:返回與此鎖相關聯的給定條件等待的線程數的估計。查詢是否有線程正在等待獲取此鎖。為公平鎖,為非公平鎖線程運行了獲得鎖定運行結果公平鎖的運行結果是有序的。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchronized關鍵字(2) Java多線程學習(三)volatile關鍵字 ...
摘要:本文只介紹中線程池的基本使用,不會過多的涉及到線程池的原理。可緩存線程的線程池創建一個可緩存線程的線程池。首先是從接口繼承到的方法使用該方法即將一個任務交給線程池去執行。方法方法的作用是向線程池發送關閉的指令。 首先,我們為什么需要線程池?讓我們先來了解下什么是 對象池 技術。某些對象(比如線程,數據庫連接等),它們創建的代價是非常大的 —— 相比于一般對象,它們創建消耗的時間和內存都...
摘要:時,標準類庫添加了,作為對型線程池的實現。類圖用來專門定義型任務完成將大任務分割為小任務以及合并結果的工作。 JDK 1.7 時,標準類庫添加了 ForkJoinPool,作為對 Fork/Join 型線程池的實現。Fork 在英文中有 分叉 的意思,而 Join 有 合并 的意思。ForkJoinPool 的功能也是如此:Fork 將大任務分叉為多個小任務,然后讓小任務執行,Join...
摘要:典型地,和被用在等待另一個線程產生的結果的情形測試發現結果還沒有產生后,讓線程阻塞,另一個線程產生了結果后,調用使其恢復。使當前線程放棄當前已經分得的時間,但不使當前線程阻塞,即線程仍處于可執行狀態,隨時可能再次分得時間。 1、說說進程,線程,協程之間的區別 簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程.進程在執行過程中擁有獨立的內存單元...
摘要:線程執行框架啟動線程將要多線程執行的任務封裝為一個對象將其傳給一個執行框架對象從線程池中選擇線程執行工作任務。 為什么需要執行框架呢?使用一般的new方法來創建線程有什么問題呢?一般的new線程的方式一般要給出一個實現了Runnable接口的執行類,在其中重寫run()方法,然后再在將這個執行類的對象傳給線程以完成初始化,這個過程中線程的定義和執行過程其實是雜糅在一起了,而且每次new...
閱讀 774·2023-04-25 15:13
閱讀 1394·2021-11-22 12:03
閱讀 823·2021-11-19 09:40
閱讀 1903·2021-11-17 09:38
閱讀 1711·2021-11-08 13:18
閱讀 653·2021-09-02 15:15
閱讀 1763·2019-08-30 15:54
閱讀 2631·2019-08-30 11:12