1 Fork-Join
1.1 Fork/Join 體現(xiàn)了“分而治之”
什么是分而治之?
規(guī)模為N的問題亚斋,N<閾值作媚,直接解決,N>閾值帅刊,將N分解為K個小規(guī)模子問題纸泡,子問題互相對立,與原問題形式相同赖瞒,將子問題的解合并得到原問題的解
1.2 工作秘取(WorkStealing)
有A女揭、B兩個線程,A線程任務(wù)已經(jīng)做完栏饮,而B線程任務(wù)還沒有做完吧兔,這時A線程可以從B線程所屬的任務(wù)中偷取任務(wù)來做,這種工作方式可提升資源利用率袍嬉,提高性能境蔼。
1.3 Fork/Join實戰(zhàn)
1.3.1 使用的范式
其中RecursiveTask、RecursiveAction伺通、ForkJoinTask都為抽象類箍土,我們自己的任務(wù)類要根據(jù)業(yè)務(wù)需要繼承并實現(xiàn)這些類,并必須實現(xiàn)compute()方法
那么這些類有什么不同呢?
- ForkJoinTask:實現(xiàn)接口 public abstract class ForkJoinTask<V> implements Future<V>, Serializable
- RecursiveTask : 任務(wù)有返回值
繼承public abstract class RecursiveTask<V> extends ForkJoinTask<V> - RecursiveAction:任務(wù)沒有返回值
繼承 public abstract class RecursiveAction extends ForkJoinTask<Void>
compute()方法邏輯
1.3.2 實戰(zhàn)案例
1.3.2.1 Fork/Join的同步用法同時演示返回結(jié)果值:統(tǒng)計整形數(shù)組中所有元素的和
任務(wù)類
private static class SumTask extends RecursiveTask<Integer>{
private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
private int[] src; //表示我們要實際統(tǒng)計的數(shù)組
private int fromIndex;//開始統(tǒng)計的下標(biāo)
private int toIndex;//統(tǒng)計到哪里結(jié)束的下標(biāo)
public SumTask(int[] src, int fromIndex, int toIndex) {
this.src = src;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
}
@Override
protected Integer compute() {
if(toIndex-fromIndex < THRESHOLD) {//若計算的范圍小于指定閾值罐监,則直接進(jìn)行業(yè)務(wù)累加
int count = 0;
for(int i=fromIndex;i<=toIndex;i++) {
//SleepTools.ms(1);
count = count + src[i];
}
return count;
}else {//若計算的范圍大于指定閾值吴藻,則將該范圍拆分,并分別分配任務(wù)弓柱,采用遞歸的方式繼續(xù)判斷求和
//fromIndex....mid....toIndex
//1...................70....100
int mid = (fromIndex+toIndex)/2;
SumTask left = new SumTask(src,fromIndex,mid);
SumTask right = new SumTask(src,mid+1,toIndex);
invokeAll(left,right);
return left.join()+right.join();
}
}
}
調(diào)用類
ForkJoinPool pool = new ForkJoinPool();
int[] src = MakeArray.makeArray();
SumTask innerFind = new SumTask(src,0,src.length-1);
long start = System.currentTimeMillis();
pool.invoke(innerFind);//同步調(diào)用
System.out.println("Task is Running.....");
System.out.println("The count is "+innerFind.join()
+" spend time:"+(System.currentTimeMillis()-start)+"ms");
1.3.2.2 Fork/Join的異步用法同時演示不要求返回值:遍歷指定目錄(含子目錄)尋找指定類型文件
任務(wù)類
public class FindDirsFiles extends RecursiveAction{
private File path;//當(dāng)前任務(wù)需要搜尋的目錄
public FindDirsFiles(File path) {
this.path = path;
}
@Override
protected void compute() {
List<FindDirsFiles> subTasks = new ArrayList<>();
File[] files = path.listFiles();
if(files!=null) {
for(File file:files) {
if(file.isDirectory()) {//如果是文件夾沟堡,則繼續(xù)調(diào)用任務(wù),遞歸處理
subTasks.add(new FindDirsFiles(file));
}else {//如果是文件矢空,則直接進(jìn)行業(yè)務(wù)邏輯
//遇到文件航罗,檢查
if(file.getAbsolutePath().endsWith("txt")) {
System.out.println("文件:"+file.getAbsolutePath());
}
}
}
if(!subTasks.isEmpty()) {//如果當(dāng)前任務(wù)中包含子任務(wù),則調(diào)用子任務(wù)join()方法,等待子任務(wù)結(jié)束當(dāng)前任務(wù)方可繼續(xù)執(zhí)行
for(FindDirsFiles subTask:invokeAll(subTasks)) {
subTask.join();//等待子任務(wù)執(zhí)行完成
}
}
}
}
}
調(diào)用類
public static void main(String [] args){
try {
// 用一個 ForkJoinPool 實例調(diào)度總?cè)蝿?wù)
ForkJoinPool pool = new ForkJoinPool();
FindDirsFiles task = new FindDirsFiles(new File("F:/"));
pool.execute(task);//異步調(diào)用
System.out.println("Task is Running......");
Thread.sleep(1);
int otherWork = 0;
for(int i=0;i<100;i++){
otherWork = otherWork+i;
}
System.out.println("Main Thread done sth......,otherWork="+otherWork);
task.join();//阻塞的方法妇多,保證主線程等待我們的異步任務(wù)線程伤哺,防止主線程提前結(jié)束
System.out.println("Task end");
} catch (Exception e) {
e.printStackTrace();
}
}
2 常用的并發(fā)工具類
2.1 CountDownLatch
CountDownLatch是一個非常實用的多線程控制工具類燕侠。常用的就下面幾個方法:
- CountDownLatch(int count) //實例化一個倒計數(shù)器者祖,count指定計數(shù)個數(shù)
- countDown() // 計數(shù)減一
- await() //等待立莉,當(dāng)計數(shù)減到0時,所有線程并行執(zhí)行
2.1.1 作用
是一組線程等待其他的線程完成工作以后在執(zhí)行七问,加強(qiáng)版join
2.1.2 應(yīng)用場景
2.1.3 實戰(zhàn)
public class CountDownLatchDemo implements Runnable{
static final CountDownLatch latch = new CountDownLatch(10);//構(gòu)造方法傳入計數(shù)器的值
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
// 模擬業(yè)務(wù)邏輯蜓耻,業(yè)務(wù)執(zhí)行完調(diào)用latch.countDown();
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("check complete");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//計數(shù)減一
//放在finally避免任務(wù)執(zhí)行過程出現(xiàn)異常,導(dǎo)致countDown()不能被執(zhí)行
latch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);//創(chuàng)建固定大小的線程池
for (int i=0; i<10; i++){
exec.submit(demo);
}
// 等待10個業(yè)務(wù)扣減完畢
latch.await();
// 主線程繼續(xù)執(zhí)行
System.out.println("扣減完畢械巡,主線程可繼續(xù)執(zhí)行");
// 關(guān)閉線程池
exec.shutdown();
}
}
2.2 CyclicBarrier
2.2.1 作用
讓一組線程達(dá)到某個屏障刹淌,被阻塞,一直到組內(nèi)最后一個線程達(dá)到屏障時讥耗,屏障開放有勾,所有被阻塞的線程會繼續(xù)運(yùn)行
CyclicBarrier(可重用屏障/柵欄) 類似于 [CountDownLatch](倒計數(shù)閉鎖),它能阻塞一組線程直到某個事件的發(fā)生古程。
* 與閉鎖的關(guān)鍵區(qū)別在于蔼卡,所有的線程必須同時到達(dá)屏障位置,才能繼續(xù)執(zhí)行挣磨。
* 閉鎖用于等待事件雇逞,而屏障用于等待其他線程。
* CyclicBarrier 可以使一定數(shù)量的線程反復(fù)地在屏障位置處匯集茁裙。當(dāng)線程到達(dá)屏障位置時將調(diào)用 await()
方法塘砸,這個方法將阻塞直到所有線程都到達(dá)屏障位置。如果所有線程都到達(dá)屏障位置晤锥,那么屏障將打開掉蔬,此時所有的線程都將被釋放,而屏障將被重置以便下次使用矾瘾。
CyclicBarrier 是 JDK 1.5 的 java.util.concurrent 并發(fā)包中提供的一個并發(fā)工具類眉踱。
* 所謂 Cyclic 即循環(huán)的意思,所謂 Barrier 即屏障的意思霜威。
* CyclicBarrier 是一個同步輔助類乙漓,它允許一組線程相互等待直到所有線程都到達(dá)一個公共的屏障點蚊荣。
* 在程序中有固定數(shù)量的線程,這些線程有時候必須等待彼此,這種情況下讲仰,使用 CyclicBarrier 很有幫助。
* 這個屏障之所以用循環(huán)修飾疼进,是因為在所有的線程釋放彼此之后诅需,這個屏障是可以 重新使用 的。
注:CyclicBarrier在本文只做簡單理解挽绩,有一些借鑒自這位博主的文章膛壹,CyclicBarrier他的這篇文章講解比較全面http://www.reibang.com/p/9262361a1200
2.2.2 應(yīng)用場景
CyclicBarrier 常用于多線程分組計算。
比如一個大型的任務(wù),常常需要分配好多子任務(wù)去執(zhí)行模聋,只有當(dāng)所有子任務(wù)都執(zhí)行完成時候肩民,才能執(zhí)行主任務(wù),這時候链方,就可以選擇 CyclicBarrier持痰。
2.2.3 CyclicBarrier 方法說明
CyclicBarrier(parties) 方法
- 初始化相互等待的線程數(shù)量的構(gòu)造方法。
CyclicBarrier(parties,Runnable barrierAction) 方法
- 初始化相互等待的線程數(shù)量以及屏障線程的構(gòu)造方法祟蚀。
- 屏障線程的運(yùn)行時機(jī):等待的線程數(shù)量 =parties 之后工窍,CyclicBarrier 打開屏障之前。
例如在分組計算中前酿,每個線程負(fù)責(zé)一部分計算患雏,最終這些線程計算結(jié)束之后,交由屏障線程進(jìn)行匯總計算罢维。
getParties 方法
- 獲取 CyclicBarrier 打開屏障的線程數(shù)量纵苛。
getNumberWaiting 方法
- 獲取正在 CyclicBarrier 上等待的線程數(shù)量。
await 方法
在 CyclicBarrier 上進(jìn)行阻塞等待言津,直到發(fā)生以下情形之一攻人。
- 在 CyclicBarrier 上等待的線程數(shù)量達(dá)到 parties,則所有線程被釋放悬槽,繼續(xù)執(zhí)行怀吻。
- 當(dāng)前線程被中斷,則拋出 InterruptedException 異常初婆,并停止等待蓬坡,繼續(xù)執(zhí)行。
- 其他等待的線程被中斷磅叛,則當(dāng)前線程拋出 BrokenBarrierException 異常屑咳,并停止等待,繼續(xù)執(zhí)行弊琴。
- 其他等待的線程超時兆龙,則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待敲董,繼續(xù)執(zhí)行紫皇。
- 其他線程調(diào)用 CyclicBarrier.reset() 方法,則當(dāng)前線程拋出 BrokenBarrierException 異常腋寨,并停止等待聪铺,繼續(xù)執(zhí)行。
- 線程調(diào)用 await() 表示自己已經(jīng)到達(dá)柵欄萄窜。
- BrokenBarrierException 表示柵欄已經(jīng)被破壞铃剔,破壞的原因可能是其中一個線程
- await() 時被中斷或者超時撒桨。
await(timeout,TimeUnit) 方法
在 CyclicBarrier 上進(jìn)行限時的阻塞等待,直到發(fā)生以下情形之一键兜。
- 在 CyclicBarrier 上等待的線程數(shù)量達(dá)到 parties凤类,則所有線程被釋放,繼續(xù)執(zhí)行蝶押。
- 當(dāng)前線程被中斷,則拋出 InterruptedException 異常棋电,并停止等待榆浓,繼續(xù)執(zhí)行。
- 當(dāng)前線程等待超時,則拋出 TimeoutException 異常,并停止等待品擎,繼續(xù)執(zhí)行蜜猾。
- 其他等待的線程被中斷,則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待印屁,繼續(xù)執(zhí)行础钠。
- 其他等待的線程超時停局,則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待,繼續(xù)執(zhí)行。
- 其他線程調(diào)用 CyclicBarrier.reset() 方法槽畔,則當(dāng)前線程拋出 BrokenBarrierException 異常早直,并停止等待枫振,繼續(xù)執(zhí)行杖小。
isBroken 方法
獲取是否破損標(biāo)志位 broken 的值伟件,此值有以下幾種情況嗓袱。
- CyclicBarrier 初始化時蝙昙,broken=false,表示屏障未破損梧却。
- 如果正在等待的線程被中斷奇颠,則 broken=true,表示屏障破損放航。
- 如果正在等待的線程超時烈拒,則 broken=true,表示屏障破損广鳍。
- 如果有線程調(diào)用 CyclicBarrier.reset() 方法荆几,則 broken=false,表示屏障回到未破損狀態(tài)赊时。
reset 方法
使 CyclicBarrier 回歸初始狀態(tài)吨铸,它做了兩件事。
- 如果有正在等待的線程祖秒,則會拋出 BrokenBarrierException 異常焊傅,且這些線程停止等待剂陡,繼續(xù)執(zhí)行。
將是否破損標(biāo)志位 broken 置為 false狐胎。
2.2.3 實戰(zhàn)
public class CyclicBarrierDemo {
// 自定義工作線程
private static class Cyclic extends Thread {
private CyclicBarrier cyclicBarrier;
public Cyclic(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
super.run();
try {
cyclicBarrier.await();//調(diào)用等待方法鸭栖,等待其他線程到達(dá)屏障
System.out.println(Thread.currentThread().getName() + "等待完畢,開始執(zhí)行");
// 模擬業(yè)務(wù)
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount);
for (int i = 0; i < threadCount; i++) {
System.out.println("創(chuàng)建工作線程" + i);
Cyclic cyclic = new Cyclic(cyclicBarrier);
cyclic.start();
}
}
}
2.3 CountDownLatch和CyclicBarrier辨析
- countdownlatch放行由第三者控制握巢,CyclicBarrier放行由一組線程本身控制
- countdownlatch放行條件》=線程數(shù)晕鹊,CyclicBarrier放行條件=線程數(shù)
- CountDownLatch 是一個線程(或者多個),等待另外 N 個線程完成某個事情之后才能執(zhí)行暴浦;CyclicBarrier 是 N 個線程相互等待溅话,任何一個線程完成之前,所有的線程都必須等待歌焦。
- CountDownLatch 的計數(shù)器只能使用一次飞几。而 CyclicBarrier 的計數(shù)器可以使用 reset() 方法重置;
- CyclicBarrier 能處理更為復(fù)雜的業(yè)務(wù)場景独撇,比如如果計算發(fā)生錯誤屑墨,可以重置計數(shù)器,并讓線程們重新執(zhí)行一次纷铣。
- CountDownLatch 采用減計數(shù)方式卵史;CyclicBarrier 采用加計數(shù)方式。
2.4 Semaphore
以一個停車場是運(yùn)作為例搜立。為了簡單起見以躯,假設(shè)停車場只有三個車位,一開始三個車位都是空的啄踊。這時如果同時來了五輛車忧设,看門人允許其中三輛不受阻礙的進(jìn)入,然后放下車攔颠通,剩下的車則必須在入口等待址晕,此后來的車也都不得不在入口處等待。這時蒜哀,有一輛車離開停車場斩箫,看門人得知后,打開車攔撵儿,放入一輛乘客,如果又離開兩輛,則又可以放入兩輛淀歇,如此往復(fù)易核。這個停車系統(tǒng)中,每輛車就好比一個線程浪默,看門人就好比一個信號量牡直,看門人限制了可以活動的線程缀匕。假如里面依然是三個車位,但是看門人改變了規(guī)則碰逸,要求每次只能停兩輛車乡小,那么一開始進(jìn)入兩輛車,后面得等到有車離開才能有車進(jìn)入饵史,但是得保證最多停兩輛車满钟。對于Semaphore類而言,就如同一個看門人胳喷,限制了可活動的線程數(shù)湃番。
Semaphore主要用于控制當(dāng)前活動線程數(shù)目,就如同停車場系統(tǒng)一般吭露,而Semaphore則相當(dāng)于看守的人吠撮,用于控制總共允許停車的停車位的個數(shù),而對于每輛車來說就如同一個線程讲竿,線程需要通過acquire()方法獲取許可泥兰,而release()釋放許可。如果許可數(shù)達(dá)到最大活動數(shù)戴卜,那么調(diào)用acquire()之后逾条,便進(jìn)入等待隊列琢岩,等待已獲得許可的線程釋放許可投剥,從而使得多線程能夠合理的運(yùn)行。
2.4.1 作用
Semaphore主要方法:
Semaphore(int permits):
構(gòu)造方法担孔,創(chuàng)建具有給定許可數(shù)的計數(shù)信號量并設(shè)置為非公平信號量江锨。
Semaphore(int permits,boolean fair):
構(gòu)造方法,當(dāng)fair等于true時糕篇,創(chuàng)建具有給定許可數(shù)的計數(shù)信號量并設(shè)置為公平信號量啄育。
void acquire():
從此信號量獲取一個許可前線程將一直阻塞。相當(dāng)于一輛車占了一個車位拌消。
void acquire(int n):
從此信號量獲取給定數(shù)目許可挑豌,在提供這些許可前一直將線程阻塞。比如n=2墩崩,就相當(dāng)于一輛車占了兩個車位氓英。
void release():
釋放一個許可,將其返回給信號量鹦筹。就如同車開走返回一個車位铝阐。
void release(int n):
釋放n個許可。
int availablePermits():
當(dāng)前可用的許可數(shù)铐拐。
getQueueLength():
等待線程隊列長度
2.4.2 應(yīng)用場景
2.4.3 實戰(zhàn)
public class SemaphoreDemo {
private static final Semaphore semaphore=new Semaphore(3);
private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
private static class InformationThread extends Thread{
private final String name;
private final int age;
public InformationThread(String name,int age)
{
this.name=name;
this.age=age;
}
public void run()
{
try
{
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+":大家好徘键,我是"+name+"我今年"+age+"歲當(dāng)前時間為:"+System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(name+"要準(zhǔn)備釋放許可證了练对,當(dāng)前時間為:"+System.currentTimeMillis());
System.out.println("當(dāng)前可使用的許可數(shù)為:"+semaphore.availablePermits());
semaphore.release();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
String[] name= {"李明","王五","張杰","王強(qiáng)","趙二","李四","張三"};
int[] age= {26,27,33,45,19,23,41};
for(int i=0;i<7;i++)
{
Thread t1=new InformationThread(name[i],age[i]);
threadPool.execute(t1);
}
}
2.5 Exchange
2.5.1 作用
Exchanger 是 JDK 1.5 開始提供的一個用于兩個工作線程之間交換數(shù)據(jù)的封裝工具類,簡單說就是一個線程在完成一定的事務(wù)后想與另一個線程交換數(shù)據(jù)吹害,則第一個先拿出數(shù)據(jù)的線程會一直等待第二個線程螟凭,直到第二個線程拿著數(shù)據(jù)到來時才能彼此交換對應(yīng)數(shù)據(jù)。其定義為 Exchanger<V> 泛型類型它呀,其中 V 表示可交換的數(shù)據(jù)類型赂摆,對外提供的接口很簡單,具體如下:
Exchanger():無參構(gòu)造方法钟些。
V exchange(V v):等待另一個線程到達(dá)此交換點(除非當(dāng)前線程被中斷)烟号,然后將給定的對象傳送給該線程,并接收該線程的對象政恍。
V exchange(V v, long timeout, TimeUnit unit):等待另一個線程到達(dá)此交換點(除非當(dāng)前線程被中斷或超出了指定的等待時間)汪拥,然后將給定的對象傳送給該線程,并接收該線程的對象篙耗。
可以看出迫筑,當(dāng)一個線程到達(dá) exchange 調(diào)用點時,如果其他線程此前已經(jīng)調(diào)用了此方法宗弯,則其他線程會被調(diào)度喚醒并與之進(jìn)行對象交換脯燃,然后各自返回;如果其他線程還沒到達(dá)交換點蒙保,則當(dāng)前線程會被掛起辕棚,直至其他線程到達(dá)才會完成交換并正常返回,或者當(dāng)前線程被中斷或超時返回邓厕。
2.5.2 應(yīng)用場景
2.5.3 實戰(zhàn)
public class UseExchange {
private static final Exchanger<String> exchange
= new Exchanger<String>();
public static void main(String[] args) {
System.out.println("ssssssssssss");
//第一個線程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setA = new HashSet<String>();//存放數(shù)據(jù)的容器
String dataA = "111";
try {
/*添加數(shù)據(jù)
* set.add(.....)
* */
System.out.println("數(shù)據(jù)交換前:"+dataA);
dataA = exchange.exchange(dataA);//交換data
System.out.println("數(shù)據(jù)交換前:"+dataA);
/*處理交換后的數(shù)據(jù)*/
} catch (InterruptedException e) {
}
}
}).start();
//第二個線程
new Thread(new Runnable() {
@Override
public void run() {
Set<String> setB = new HashSet<String>();//存放數(shù)據(jù)的容器
String dataB = "222";
try {
/*添加數(shù)據(jù)
* set.add(.....)
* set.add(.....)
* */
System.out.println("數(shù)據(jù)交換前:"+dataB);
dataB = exchange.exchange(dataB);//交換data
System.out.println("數(shù)據(jù)交換前:"+dataB);
/*處理交換后的數(shù)據(jù)*/
} catch (InterruptedException e) {
}
}
}).start();
}
}
2.6 Callable逝嚎、Future和FutureTask
2.6.1 繼承結(jié)構(gòu)圖
2.6.2 Future接口常用方法
V get();阻塞方法,獲取Callable的返回值
V get(long,Timeunit);設(shè)定超時時間和超時操作
isDone()详恼,結(jié)束补君,正常還是異常結(jié)束,或者自己取消昧互,返回true挽铁;
isCancelled() 任務(wù)完成前被取消,返回true敞掘;
cancel(boolean):
- 任務(wù)還沒開始叽掘,返回false
- 任務(wù)已經(jīng)啟動,cancel(true)渐逃,中斷正在運(yùn)行的任務(wù)够掠,中斷成功,返回true茄菊,cancel(false)疯潭,不會去中斷已經(jīng)運(yùn)行的任務(wù)
- 任務(wù)已經(jīng)結(jié)束赊堪,返回false
2.6.3 實戰(zhàn)案例
public class UseFuture {
/*實現(xiàn)Callable接口,允許有返回值*/
private static class UseCallable implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子線程開始計算");
Thread.sleep(2000);
for(int i=0;i<5000;i++) {
sum = sum+i;
}
System.out.println("Callable子線程計算完成竖哩,結(jié)果="+sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();//創(chuàng)建自定義的Callable
FutureTask<Integer> futureTask = new FutureTask<Integer>(useCallable);//新建FutureTask
new Thread(futureTask).start();//將FutureTask通過線程啟動
Random r = new Random();
SleepTools.second(1);
if(r.nextBoolean()) {//隨機(jī)決定是獲得結(jié)果還是終止任務(wù)
System.out.println("Get UseCallable result = "+futureTask.get());//阻塞獲取Callable中call()方法的返回值
}else {
System.out.println("中斷計算");
futureTask.cancel(true);
}
}
}
2.6.4 應(yīng)用場景
包含圖片和文字的文檔的處理:圖片(云上)哭廉,可以用future去取圖片,主線程繼續(xù)解析文字相叁。