Java 并發(fā)工具包-常用線程池

image.png

1. 執(zhí)行器服務(wù) ExecutorService

java.util.concurrent.ExecutorService 接口表示一個異步執(zhí)行機制贫贝,使我們能夠在后臺執(zhí)行任務(wù)冯事。因此一個 ExecutorService 很類似于一個線程池惰帽。實際上盈咳,存在于 java.util.concurrent 包里的 ExecutorService 實現(xiàn)就是一個線程池實現(xiàn)汛蝙。

ExecutorService 例子

以下是一個簡單的 ExecutorService 例子:


ExecutorService executorService = Executors.newFixedThreadPool(10);  
  
executorService.execute(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
  
executorService.shutdown();  

首先使用 newFixedThreadPool() 工廠方法創(chuàng)建一個 ExecutorService故俐。這里創(chuàng)建了一個十個線程執(zhí)行任務(wù)的線程池胁澳。然后鹿驼,將一個 Runnable 接口的匿名實現(xiàn)類傳遞給 execute() 方法欲低。這將導(dǎo)致 ExecutorService 中的某個線程執(zhí)行該 Runnable。

任務(wù)委派

下圖說明了一個線程是如何將一個任務(wù)委托給一個 ExecutorService 去異步執(zhí)行的:

image.png

一個線程將一個任務(wù)委派給一個 ExecutorService 去異步執(zhí)行畜晰。

一旦該線程將任務(wù)委派給 ExecutorService砾莱,該線程將繼續(xù)它自己的執(zhí)行,獨立于該任務(wù)的執(zhí)行凄鼻。

ExecutorService 實現(xiàn)

既然 ExecutorService 是個接口腊瑟,如果你想用它的話就得去使用它的實現(xiàn)類之一。

java.util.concurrent 包提供了 ExecutorService 接口的以下實現(xiàn)類:

創(chuàng)建一個 ExecutorService

ExecutorService 的創(chuàng)建依賴于你使用的具體實現(xiàn)块蚌。但是你也可以使用 Executors 工廠類來創(chuàng)建 ExecutorService 實例闰非。

以下是幾個創(chuàng)建 ExecutorService 實例的例子:


ExecutorService executorService1 = Executors.newSingleThreadExecutor();  
  
ExecutorService executorService2 = Executors.newFixedThreadPool(10);  
  
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);  

ExecutorService 使用

有幾種不同的方式來將任務(wù)委托給 ExecutorService 去執(zhí)行:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(...)
  • invokeAll(...)

接下來我們挨個看一下這些方法。

execute(Runnable)

execute(Runnable) 方法要求一個 java.lang.Runnable 對象峭范,然后對它進行異步執(zhí)行财松。以下是使用 ExecutorService 執(zhí)行一個 Runnable 的示例:


ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
executorService.execute(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
  
executorService.shutdown();  

沒有辦法得知被執(zhí)行的 Runnable 的執(zhí)行結(jié)果。如果有需要的話你得使用一個 Callable(以下將做介紹)纱控。

submit(Runnable)

submit(Runnable) 方法也要求一個 Runnable 實現(xiàn)類游岳,但它返回一個 Future 對象。這個 Future 對象可以用來檢查 Runnable 是否已經(jīng)執(zhí)行完畢其徙。以下是 ExecutorService submit() 示例:


Future future = executorService.submit(new Runnable() {  
    public void run() {  
        System.out.println("Asynchronous task");  
    }  
});  
  
future.get();  //returns null if the task has finished correctly.  

submit(Callable)

submit(Callable) 方法類似于 submit(Runnable) 方法胚迫,除了它所要求的參數(shù)類型之外。Callable 實例除了它的 call() 方法能夠返回一個結(jié)果之外和一個 Runnable 很相像唾那。Runnable.run() 不能夠返回一個結(jié)果访锻。Callable 的結(jié)果可以通過 submit(Callable) 方法返回的 Future 對象進行獲取褪尝。

以下是一個 ExecutorService Callable 示例:


Future future = executorService.submit(new Callable(){  
    public Object call() throws Exception {  
        System.out.println("Asynchronous Callable");  
        return "Callable Result";  
    }  
});  
  
System.out.println("future.get() = " + future.get());  

以上代碼輸出:
Asynchronous Callable
future.get() = Callable Result

invokeAny()

invokeAny() 方法要求一系列的 Callable 或者其子接口的實例對象。調(diào)用這個方法并不會返回一個 Future期犬,但它返回其中一個 Callable 對象的結(jié)果河哑。無法保證返回的是哪個 Callable 的結(jié)果 - 只能表明其中一個已執(zhí)行結(jié)束。

如果其中一個任務(wù)執(zhí)行結(jié)束(或者拋了一個異常)龟虎,其他 Callable 將被取消璃谨。以下是示例代碼:


ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 1";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 2";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 3";  
    }  
});  
  
String result = executorService.invokeAny(callables);  
  
System.out.println("result = " + result);  
  
executorService.shutdown();  

上述代碼將會打印出給定 Callable 集合中的一個的執(zhí)行結(jié)果。我自己試著執(zhí)行了它幾次鲤妥,結(jié)果始終在變佳吞。有時是 "Task 1",有時是 "Task 2" 等等棉安。

invokeAll()

invokeAll() 方法將調(diào)用你在集合中傳給 ExecutorService 的所有 Callable 對象底扳。invokeAll() 返回一系列的 Future 對象,通過它們你可以獲取每個 Callable 的執(zhí)行結(jié)果贡耽。記住衷模,一個任務(wù)可能會由于一個異常而結(jié)束,因此它可能沒有 "成功"蒲赂。

無法通過一個 Future 對象來告知我們是兩種結(jié)束中的哪一種阱冶。以下是一個代碼示例:


ExecutorService executorService = Executors.newSingleThreadExecutor();  
  
Set<Callable<String>> callables = new HashSet<Callable<String>>();  
  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 1";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 2";  
    }  
});  
callables.add(new Callable<String>() {  
    public String call() throws Exception {  
        return "Task 3";  
    }  
});  
  
List<Future<String>> futures = executorService.invokeAll(callables);  
  
for(Future<String> future : futures){  
    System.out.println("future.get = " + future.get());  
}  
  
executorService.shutdown();  

ExecutorService 關(guān)閉

使用完 ExecutorService 之后你應(yīng)該將其關(guān)閉,以使其中的線程不再運行滥嘴。

比如片吊,如果你的應(yīng)用是通過一個 main() 方法啟動的墨闲,之后 main 方法退出了你的應(yīng)用盟榴,如果你的應(yīng)用有一個活動的 ExexutorService 它將還會保持運行磺芭。ExecutorService 里的活動線程阻止了 JVM 的關(guān)閉。

要終止 ExecutorService 里的線程你需要調(diào)用 ExecutorService 的 shutdown() 方法是尖。ExecutorService 并不會立即關(guān)閉意系,但它將不再接受新的任務(wù),而且一旦所有線程都完成了當前任務(wù)的時候饺汹,ExecutorService 將會關(guān)閉蛔添。在 shutdown() 被調(diào)用之前所有提交給 ExecutorService 的任務(wù)都被執(zhí)行。如果你想要立即關(guān)閉 ExecutorService兜辞,你可以調(diào)用 shutdownNow() 方法迎瞧。這樣會立即嘗試停止所有執(zhí)行中的任務(wù),并忽略掉那些已提交但尚未開始處理的任務(wù)逸吵。無法擔保執(zhí)行任務(wù)的正確執(zhí)行凶硅。可能它們被停止了扫皱,也可能已經(jīng)執(zhí)行結(jié)束足绅。

2. 線程池執(zhí)行者 ThreadPoolExecutor

java.util.concurrent.ThreadPoolExecutor 是 ExecutorService 接口的一個實現(xiàn)捷绑。ThreadPoolExecutor 使用其內(nèi)部池中的線程執(zhí)行給定任務(wù)(Callable 或者 Runnable)。

ThreadPoolExecutor 包含的線程池能夠包含不同數(shù)量的線程氢妈。池中線程的數(shù)量由以下變量決定:

  • corePoolSize
  • maximumPoolSize

當一個任務(wù)委托給線程池時粹污,如果池中線程數(shù)量低于 corePoolSize,一個新的線程將被創(chuàng)建首量,即使池中可能尚有空閑線程壮吩。如果內(nèi)部任務(wù)隊列已滿,而且有至少 corePoolSize 正在運行加缘,但是運行線程的數(shù)量低于 maximumPoolSize鸭叙,一個新的線程將被創(chuàng)建去執(zhí)行該任務(wù)。

ThreadPoolExecutor 圖解:

image.png

** 一個 ThreadPoolExecutor **

創(chuàng)建一個 ThreadPoolExecutor

ThreadPoolExecutor 有若干個可用構(gòu)造子生百。比如:


int  corePoolSize  =    5;  
int  maxPoolSize   =   10;  
long keepAliveTime = 5000;  
  
ExecutorService threadPoolExecutor =  
        new ThreadPoolExecutor(  
                corePoolSize,  
                maxPoolSize,  
                keepAliveTime,  
                TimeUnit.MILLISECONDS,  
                new LinkedBlockingQueue<Runnable>()  
                );  

但是递雀,除非你確實需要顯式為 ThreadPoolExecutor 定義所有參數(shù)柄延,使用 java.util.concurrent.Executors 類中的工廠方法之一會更加方便蚀浆,正如 ExecutorService 小節(jié)所述。

3. 定時執(zhí)行者服務(wù) ScheduledExecutorService

java.util.concurrent.ScheduledExecutorService 是一個 ExecutorService搜吧, 它能夠?qū)⑷蝿?wù)延后執(zhí)行市俊,或者間隔固定時間多次執(zhí)行。 任務(wù)由一個工作者線程異步執(zhí)行滤奈,而不是由提交任務(wù)給 ScheduledExecutorService 的那個線程執(zhí)行摆昧。

ScheduledExecutorService 例子

以下是一個簡單的 ScheduledExecutorService 示例:

ScheduledExecutorService scheduledExecutorService =  
        Executors.newScheduledThreadPool(5);  
  
ScheduledFuture scheduledFuture =  
    scheduledExecutorService.schedule(new Callable() {  
        public Object call() throws Exception {  
            System.out.println("Executed!");  
            return "Called!";  
        }  
    },  
    5,  
    TimeUnit.SECONDS);  

首先一個內(nèi)置 5 個線程的 ScheduledExecutorService 被創(chuàng)建。之后一個 Callable 接口的匿名類示例被創(chuàng)建然后傳遞給 schedule() 方法蜒程。后邊的倆參數(shù)定義了 Callable 將在 5 秒鐘之后被執(zhí)行绅你。

ScheduledExecutorService 實現(xiàn)

既然 ScheduledExecutorService 是一個接口,你要用它的話就得使用 java.util.concurrent 包里對它的某個實現(xiàn)類昭躺。ScheduledExecutorService 具有以下實現(xiàn)類:ScheduledThreadPoolExecutor

創(chuàng)建一個 ScheduledExecutorService
如何創(chuàng)建一個 ScheduledExecutorService 取決于你采用的它的實現(xiàn)類忌锯。但是你也可以使用 Executors 工廠類來創(chuàng)建一個 ScheduledExecutorService 實例。比如:


ScheduledExecutorService scheduledExecutorService =  
  
        Executors.newScheduledThreadPool(5);  

ScheduledExecutorService 使用

一旦你創(chuàng)建了一個 ScheduledExecutorService领炫,你可以通過調(diào)用它的以下方法:

  • schedule (Callable task, long delay, TimeUnit timeunit)
  • schedule (Runnable task, long delay, TimeUnit timeunit)
  • scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
  • scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

下面我們就簡單看一下這些方法偶垮。

schedule (Callable task, long delay, TimeUnit timeunit)

這個方法計劃指定的 Callable 在給定的延遲之后執(zhí)行。這個方法返回一個 ScheduledFuture帝洪,通過它你可以在它被執(zhí)行之前對它進行取消似舵,或者在它執(zhí)行之后獲取結(jié)果。以下是一個示例:


ScheduledExecutorService scheduledExecutorService =  
        Executors.newScheduledThreadPool(5);  
  
ScheduledFuture scheduledFuture =  
    scheduledExecutorService.schedule(new Callable() {  
        public Object call() throws Exception {  
            System.out.println("Executed!");  
            return "Called!";  
        }  
    },  
    5,  
    TimeUnit.SECONDS);  
  
System.out.println("result = " + scheduledFuture.get());  
  
scheduledExecutorService.shutdown();  

示例輸出結(jié)果:
Executed!
result = Called!

schedule (Runnable task, long delay, TimeUnit timeunit)

除了 Runnable 無法返回一個結(jié)果之外葱峡,這一方法工作起來就像以一個 Callable 作為一個參數(shù)的那個版本的方法一樣砚哗,因此 ScheduledFuture.get() 在任務(wù)執(zhí)行結(jié)束之后返回 null。

scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

這一方法規(guī)劃一個任務(wù)將被定期執(zhí)行砰奕。該任務(wù)將會在首個 initialDelay 之后得到執(zhí)行蛛芥,然后每個 period 時間之后重復(fù)執(zhí)行泌参。如果給定任務(wù)的執(zhí)行拋出了異常,該任務(wù)將不再執(zhí)行常空。如果沒有任何異常的話沽一,這個任務(wù)將會持續(xù)循環(huán)執(zhí)行到 ScheduledExecutorService 被關(guān)閉。如果一個任務(wù)占用了比計劃的時間間隔更長的時候漓糙,下一次執(zhí)行將在當前執(zhí)行結(jié)束執(zhí)行才開始铣缠。計劃任務(wù)在同一時間不會有多個線程同時執(zhí)行。

scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

除了 period 有不同的解釋之外這個方法和 scheduleAtFixedRate() 非常像昆禽。

scheduleAtFixedRate() 方法中蝗蛙,period 被解釋為前一個執(zhí)行的開始和下一個執(zhí)行的開始之間的間隔時間。而在本方法中醉鳖,period 則被解釋為前一個執(zhí)行的結(jié)束和下一個執(zhí)行的結(jié)束之間的間隔捡硅。因此這個延遲是執(zhí)行結(jié)束之間的間隔,而不是執(zhí)行開始之間的間隔盗棵。

ScheduledExecutorService 關(guān)閉

正如 ExecutorService壮韭,在你使用結(jié)束之后你需要把 ScheduledExecutorService 關(guān)閉掉。否則他將導(dǎo)致 JVM 繼續(xù)運行纹因,即使所有其他線程已經(jīng)全被關(guān)閉喷屋。

你可以使用從 ExecutorService 接口繼承來的 shutdown() 或 shutdownNow() 方法將 ScheduledExecutorService 關(guān)閉。參見 ExecutorService 關(guān)閉部分以獲取更多信息瞭恰。

4. 使用 ForkJoinPool 進行分叉和合并

ForkJoinPool 在 Java 7 中被引入屯曹。它和 ExecutorService 很相似,除了一點不同惊畏。ForkJoinPool 讓我們可以很方便地把任務(wù)分裂成幾個更小的任務(wù)恶耽,這些分裂出來的任務(wù)也將會提交給 ForkJoinPool。任務(wù)可以繼續(xù)分割成更小的子任務(wù)颜启,只要它還能分割偷俭。可能聽起來有些抽象农曲,因此本節(jié)中我們將會解釋 ForkJoinPool 是如何工作的社搅,還有任務(wù)分割是如何進行的。

分叉和合并解釋

在我們開始看 ForkJoinPool 之前我們先來簡要解釋一下分叉和合并的原理乳规。
分叉和合并原理包含兩個遞歸進行的步驟形葬。兩個步驟分別是分叉步驟和合并步驟。

分叉

一個使用了分叉和合并原理的任務(wù)可以將自己分叉(分割)為更小的子任務(wù)暮的,這些子任務(wù)可以被并發(fā)執(zhí)行笙以。如下圖所示:

image.png

通過把自己分割成多個子任務(wù),每個子任務(wù)可以由不同的 CPU 并行執(zhí)行冻辩,或者被同一個 CPU 上的不同線程執(zhí)行猖腕。只有當給的任務(wù)過大拆祈,把它分割成幾個子任務(wù)才有意義。把任務(wù)分割成子任務(wù)有一定開銷倘感,因此對于小型任務(wù)放坏,這個分割的消耗可能比每個子任務(wù)并發(fā)執(zhí)行的消耗還要大。

什么時候把一個任務(wù)分割成子任務(wù)是有意義的老玛,這個界限也稱作一個閥值淤年。這要看每個任務(wù)對有意義閥值的決定。很大程度上取決于它要做的工作的種類蜡豹。

合并

當一個任務(wù)將自己分割成若干子任務(wù)之后麸粮,該任務(wù)將進入等待所有子任務(wù)的結(jié)束之中。一旦子任務(wù)執(zhí)行結(jié)束镜廉,該任務(wù)可以把所有結(jié)果合并到同一個結(jié)果弄诲。圖示如下:

image.png

當然,并非所有類型的任務(wù)都會返回一個結(jié)果娇唯。如果這個任務(wù)并不返回一個結(jié)果齐遵,它只需等待所有子任務(wù)執(zhí)行完畢。也就不需要結(jié)果的合并啦视乐。

ForkJoinPool

ForkJoinPool 是一個特殊的線程池洛搀,它的設(shè)計是為了更好的配合 分叉-和-合并 任務(wù)分割的工作敢茁。ForkJoinPool 也在 java.util.concurrent 包中佑淀,其完整類名為 java.util.concurrent.ForkJoinPool。

創(chuàng)建一個 ForkJoinPool

你可以通過其構(gòu)造子創(chuàng)建一個 ForkJoinPool彰檬。作為傳遞給 ForkJoinPool 構(gòu)造子的一個參數(shù)伸刃,你可以定義你期望的并行級別。并行級別表示你想要傳遞給 ForkJoinPool 的任務(wù)所需的線程或 CPU 數(shù)量逢倍。以下是一個 ForkJoinPool 示例:


ForkJoinPool forkJoinPool = new ForkJoinPool(4);  

這個示例創(chuàng)建了一個并行級別為 4 的 ForkJoinPool捧颅。

提交任務(wù)到 ForkJoinPool

就像提交任務(wù)到 ExecutorService 那樣,把任務(wù)提交到 ForkJoinPool较雕。你可以提交兩種類型的任務(wù)碉哑。一種是沒有任何返回值的(一個 "行動"),另一種是有返回值的(一個"任務(wù)")亮蒋。這兩種類型分別由 RecursiveAction 和 RecursiveTask 表示扣典。接下來介紹如何使用這兩種類型的任務(wù),以及如何對它們進行提交慎玖。

RecursiveAction

RecursiveAction 是一種沒有任何返回值的任務(wù)贮尖。它只是做一些工作,比如寫數(shù)據(jù)到磁盤趁怔,然后就退出了湿硝。一個 RecursiveAction 可以把自己的工作分割成更小的幾塊薪前,這樣它們可以由獨立的線程或者 CPU 執(zhí)行。你可以通過繼承來實現(xiàn)一個 RecursiveAction关斜。示例如下:


import java.util.ArrayList;  
import java.util.List;  
import java.util.concurrent.RecursiveAction;  
  
public class MyRecursiveAction extends RecursiveAction {  
  
    private long workLoad = 0;  
  
    public MyRecursiveAction(long workLoad) {  
        this.workLoad = workLoad;  
    }  
  
    @Override  
    protected void compute() {  
  
        //if work is above threshold, break tasks up into smaller tasks  
        if(this.workLoad > 16) {  
            System.out.println("Splitting workLoad : " + this.workLoad);  
  
            List<MyRecursiveAction> subtasks =  
                new ArrayList<MyRecursiveAction>();  
  
            subtasks.addAll(createSubtasks());  
  
            for(RecursiveAction subtask : subtasks){  
                subtask.fork();  
            }  
  
        } else {  
            System.out.println("Doing workLoad myself: " + this.workLoad);  
        }  
    }  
  
    private List<MyRecursiveAction> createSubtasks() {  
        List<MyRecursiveAction> subtasks =  
            new ArrayList<MyRecursiveAction>();  
  
        MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);  
        MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);  
  
        subtasks.add(subtask1);  
        subtasks.add(subtask2);  
  
        return subtasks;  
    }  
  
}  

例子很簡單示括。MyRecursiveAction 將一個虛構(gòu)的 workLoad 作為參數(shù)傳給自己的構(gòu)造子。如果 workLoad 高于一個特定閥值痢畜,該工作將被分割為幾個子工作例诀,子工作繼續(xù)分割。如果 workLoad 低于特定閥值裁着,該工作將由 MyRecursiveAction 自己執(zhí)行繁涂。你可以這樣規(guī)劃一個 MyRecursiveAction 的執(zhí)行:


MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24);  
  
forkJoinPool.invoke(myRecursiveAction);  

RecursiveTask

RecursiveTask 是一種會返回結(jié)果的任務(wù)。它可以將自己的工作分割為若干更小任務(wù)二驰,并將這些子任務(wù)的執(zhí)行結(jié)果合并到一個集體結(jié)果扔罪。可以有幾個水平的分割和合并桶雀。以下是一個 RecursiveTask 示例:


import java.util.ArrayList;  
import java.util.List;  
import java.util.concurrent.RecursiveTask;  
      
      
public class MyRecursiveTask extends RecursiveTask<Long> {  
  
    private long workLoad = 0;  
  
    public MyRecursiveTask(long workLoad) {  
        this.workLoad = workLoad;  
    }  
  
    protected Long compute() {  
  
        //if work is above threshold, break tasks up into smaller tasks  
        if(this.workLoad > 16) {  
            System.out.println("Splitting workLoad : " + this.workLoad);  
  
            List<MyRecursiveTask> subtasks =  
                new ArrayList<MyRecursiveTask>();  
            subtasks.addAll(createSubtasks());  
  
            for(MyRecursiveTask subtask : subtasks){  
                subtask.fork();  
            }  
  
            long result = 0;  
            for(MyRecursiveTask subtask : subtasks) {  
                result += subtask.join();  
            }  
            return result;  
  
        } else {  
            System.out.println("Doing workLoad myself: " + this.workLoad);  
            return workLoad * 3;  
        }  
    }  
      
    private List<MyRecursiveTask> createSubtasks() {  
        List<MyRecursiveTask> subtasks =  
        new ArrayList<MyRecursiveTask>();  
  
        MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad / 2);  
        MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad / 2);  
  
        subtasks.add(subtask1);  
        subtasks.add(subtask2);  
  
        return subtasks;  
    }  
}  

除了有一個結(jié)果返回之外矿酵,這個示例和 RecursiveAction 的例子很像。MyRecursiveTask 類繼承自 RecursiveTask<Long>矗积,這也就意味著它將返回一個 Long 類型的結(jié)果全肮。

MyRecursiveTask 示例也會將工作分割為子任務(wù),并通過 fork() 方法對這些子任務(wù)計劃執(zhí)行棘捣。

此外辜腺,本示例還通過調(diào)用每個子任務(wù)的 join() 方法收集它們返回的結(jié)果。子任務(wù)的結(jié)果隨后被合并到一個更大的結(jié)果乍恐,并最終將其返回评疗。對于不同級別的遞歸,這種子任務(wù)的結(jié)果合并可能會發(fā)生遞歸茵烈。

你可以這樣規(guī)劃一個 RecursiveTask:


MyRecursiveTask myRecursiveTask = new MyRecursiveTask(128);  
  
long mergedResult = forkJoinPool.invoke(myRecursiveTask);  
  
System.out.println("mergedResult = " + mergedResult);   

注意是如何通過 ForkJoinPool.invoke() 方法的調(diào)用來獲取最終執(zhí)行結(jié)果的百匆。

ForkJoinPool 評論

貌似并非每個人都對 Java 7 里的 ForkJoinPool 滿意:《一個 Java 分叉-合并 帶來的災(zāi)禍》。

在你計劃在自己的項目里使用 ForkJoinPool 之前最好讀一下該篇文章呜投。


個人介紹:

** 高廣超** :多年一線互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計經(jīng)驗加匈,擅長設(shè)計與落地高可用、高性能互聯(lián)網(wǎng)架構(gòu)仑荐。目前就職于美團網(wǎng)雕拼,負責(zé)核心業(yè)務(wù)研發(fā)工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末释漆,一起剝皮案震驚了整個濱河市悲没,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖示姿,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甜橱,死亡現(xiàn)場離奇詭異,居然都是意外死亡栈戳,警方通過查閱死者的電腦和手機岂傲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來子檀,“玉大人镊掖,你說我怎么就攤上這事」犹担” “怎么了亩进?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缩歪。 經(jīng)常有香客問我归薛,道長,這世上最難降的妖魔是什么匪蝙? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任主籍,我火速辦了婚禮,結(jié)果婚禮上逛球,老公的妹妹穿的比我還像新娘千元。我一直安慰自己,他們只是感情好颤绕,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布幸海。 她就那樣靜靜地躺著,像睡著了一般屋厘。 火紅的嫁衣襯著肌膚如雪涕烧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天汗洒,我揣著相機與錄音,去河邊找鬼父款。 笑死溢谤,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的憨攒。 我是一名探鬼主播世杀,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肝集!你這毒婦竟也來了瞻坝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤杏瞻,失蹤者是張志新(化名)和其女友劉穎所刀,沒想到半個月后衙荐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡浮创,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年忧吟,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斩披。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡溜族,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垦沉,到底是詐尸還是另有隱情煌抒,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布厕倍,位于F島的核電站摧玫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绑青。R本人自食惡果不足惜诬像,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闸婴。 院中可真熱鬧坏挠,春花似錦、人聲如沸邪乍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庇楞。三九已至榜配,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吕晌,已是汗流浹背蛋褥。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留睛驳,地道東北人烙心。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像乏沸,于是被迫代替她去往敵國和親淫茵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容