Callable 、Future、FutureTask
Futuretask是JUC下的一個(gè)組件,針對對線程結(jié)果的處理
有兩種創(chuàng)建多線程的方式:一個(gè)是繼承Thread類,另一個(gè)是實(shí)現(xiàn)Runnable接口。但是這樣的一個(gè)問題是實(shí)現(xiàn)Runnable的run()方法后并不能返回任何結(jié)果,因?yàn)閞un()是一個(gè)void類型。在jdk1.5之后java提供callable和Future,通過它們可以得到線程任務(wù)執(zhí)行的結(jié)果
Runnable
java.lang.Runnable 它是一個(gè)接口,在它里面只聲明了一個(gè)run()方法:
public interface Runnable {
public abstract void run();
}
Callable
java.util.concurrent 它也是一個(gè)接口,在它里面也只聲明了一個(gè)方法,只不過這個(gè)方法叫做call()
這是一個(gè)泛型接口,該接口聲明了一個(gè)名稱為call()的方法,同時(shí)這個(gè)方法可以有返回值V,也可以拋出異常。call()方法返回的類型就是傳遞進(jìn)來的V類型。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
那么怎么使用Callable呢?一般情況下是配合ExecutorService來使用的,在ExecutorService接口中聲明了若干個(gè)submit方法的重載版本:
//第一個(gè)方法:submit提交一個(gè)實(shí)現(xiàn)Callable接口的任務(wù),并且返回封裝了異步計(jì)算結(jié)果的Future。
<T> Future<T> submit(Callable<T> task);
//第二個(gè)方法:submit提交一個(gè)實(shí)現(xiàn)Runnable接口的任務(wù),并且指定了在調(diào)用Future的get方法時(shí)返回的result對象。
<T> Future<T> submit(Runnable task, T result);
//第三個(gè)方法:submit提交一個(gè)實(shí)現(xiàn)Runnable接口的任務(wù),并且返回封裝了異步計(jì)算結(jié)果的Future。
Future<?> submit(Runnable task);
因此我們只要?jiǎng)?chuàng)建好我們的線程對象(實(shí)現(xiàn)Callable接口或者Runnable接口),然后通過上面3個(gè)方法提交給線程池去執(zhí)行即可。
Future
Future就是對于具體的Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成、獲取結(jié)果。必要時(shí)可以通過get方法獲取執(zhí)行結(jié)果,該方法會(huì)阻塞直到任務(wù)返回結(jié)果。
Future<V>接口是用來獲取異步計(jì)算結(jié)果的,說白了就是對具體的Runnable或者Callable對象任務(wù)執(zhí)行的結(jié)果進(jìn)行獲取(get()),取消(cancel()),判斷是否完成等操作。
public interface Future<V> {
//cancel方法用來取消任務(wù),如果取消任務(wù)成功則返回true,如果取消任務(wù)失敗則返回false。
//參數(shù)mayInterruptIfRunning表示是否允許取消正在執(zhí)行卻沒有執(zhí)行完畢的任務(wù)
//如果設(shè)置true,則表示可以取消正在執(zhí)行過程中的任務(wù)。如果任務(wù)已經(jīng)完成,則無論mayInterruptIfRunning為true還是false,此方法肯定返回false,即如果取消已經(jīng)完成的任務(wù)會(huì)返回false;
//如果任務(wù)正在執(zhí)行,若mayInterruptIfRunning設(shè)置為true,則返回true,若mayInterruptIfRunning設(shè)置為false,則返回false;
//如果任務(wù)還沒有執(zhí)行,則無論mayInterruptIfRunning為true還是false,肯定返回true。
boolean cancel(boolean mayInterruptIfRunning);
//isCancelled方法表示任務(wù)是否被取消成功,如果在任務(wù)正常完成前被取消成功,則返回 true。
boolean isCancelled();
//isDone方法表示任務(wù)是否已經(jīng)完成,若任務(wù)完成,則返回true;
boolean isDone();
//get()方法用來獲取執(zhí)行結(jié)果,這個(gè)方法會(huì)產(chǎn)生阻塞,會(huì)一直等到任務(wù)執(zhí)行完畢才返回;
V get() throws InterruptedException, ExecutionException;
//get(long timeout, TimeUnit unit)用來獲取執(zhí)行結(jié)果,如果在指定時(shí)間內(nèi),還沒獲取到結(jié)果,就直接返回null。
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future提供了三種功能:
1)判斷任務(wù)是否完成;
2)能夠中斷任務(wù);
3)能夠獲取任務(wù)執(zhí)行結(jié)果。
因?yàn)镕uture只是一個(gè)接口,所以是無法直接用來創(chuàng)建對象使用的,因此就有了下面的FutureTask。
FutureTask
創(chuàng)建一個(gè)線程Runnable是必須的,要保存線程的返回結(jié)果Future是必須的,因此有了FutureTask,F(xiàn)utureTask同時(shí)實(shí)現(xiàn)了Runnable和Future接口。
所以它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值。
我們先來看一下FutureTask的實(shí)現(xiàn):
public class FutureTask<V> implements RunnableFuture<V>
FutureTask類實(shí)現(xiàn)了RunnableFuture接口,我們看一下RunnableFuture接口的實(shí)現(xiàn):
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
分析:
FutureTask除了實(shí)現(xiàn)了Future接口外還實(shí)現(xiàn)了Runnable接口,因此FutureTask也可以直接提交給Executor執(zhí)行。 當(dāng)然也可以調(diào)用線程直接執(zhí)行(FutureTask.run())。接下來我們根據(jù)FutureTask.run()的執(zhí)行時(shí)機(jī)來分析其所處的3種狀態(tài):
(1)未啟動(dòng),F(xiàn)utureTask.run()方法還沒有被執(zhí)行之前,F(xiàn)utureTask處于未啟動(dòng)狀態(tài),當(dāng)創(chuàng)建一個(gè)FutureTask,而且沒有執(zhí)行FutureTask.run()方法前,這個(gè)FutureTask也處于未啟動(dòng)狀態(tài)。
(2)已啟動(dòng),F(xiàn)utureTask.run()被執(zhí)行的過程中,F(xiàn)utureTask處于已啟動(dòng)狀態(tài)。
(3)已完成,F(xiàn)utureTask.run()方法執(zhí)行完正常結(jié)束,或者被取消或者拋出異常而結(jié)束,F(xiàn)utureTask都處于完成狀態(tài)。
image.png
FutureTask的方法執(zhí)行示意圖

分析:
(1)當(dāng)FutureTask處于未啟動(dòng)或已啟動(dòng)狀態(tài)時(shí),如果此時(shí)我們執(zhí)行FutureTask.get()方法將導(dǎo)致調(diào)用線程阻塞;當(dāng)FutureTask處于已完成狀態(tài)時(shí),執(zhí)行FutureTask.get()方法將導(dǎo)致調(diào)用線程立即返回結(jié)果或者拋出異常。
(2)當(dāng)FutureTask處于未啟動(dòng)狀態(tài)時(shí),執(zhí)行FutureTask.cancel()方法將導(dǎo)致此任務(wù)永遠(yuǎn)不會(huì)執(zhí)行。當(dāng)FutureTask處于已啟動(dòng)狀態(tài)時(shí),執(zhí)行cancel(true)方法將以中斷執(zhí)行此任務(wù)線程的方式來試圖停止任務(wù),如果任務(wù)取消成功,cancel(...)返回true;但如果執(zhí)行cancel(false)方法將不會(huì)對正在執(zhí)行的任務(wù)線程產(chǎn)生影響(讓線程正常執(zhí)行到完成),此時(shí)cancel(...)返回false。當(dāng)任務(wù)已經(jīng)完成,執(zhí)行cancel(...)方法將返回false。
代碼實(shí)例:
使用Callable+Future獲取執(zhí)行結(jié)果
public class FutureExample {
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
}
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new MyCallable());
log.info("do something in main");
Thread.sleep(1000); //主線程睡一秒
String result = future.get(); //如果子線程沒有執(zhí)行完,主線程會(huì)一直阻塞在這里
log.info("result:{}", result);
}
}
執(zhí)行結(jié)果:
16:16:53.843 [main] do something in main
16:16:53.843 [pool-1-thread-1] do something in callable
16:16:58.846 [main] result:Done //與上一個(gè)日志打印,阻塞了5秒鐘
使用Callable+FutureTask獲取執(zhí)行結(jié)果
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() { //還可以runable
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
}
}
執(zhí)行結(jié)果,與上面相同
補(bǔ)充:
實(shí)現(xiàn)Runnable接口和實(shí)現(xiàn)Callable接口的區(qū)別:
1、Runnable是自從java1.1就有了,而Callable是1.5之后才加上去的。
2、Callable規(guī)定的方法是call(),Runnable規(guī)定的方法是run()。
3、Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值(是void)。
4、call方法可以拋出異常,run方法不可以。
5、運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過Future對象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果。
6、需要注意的是,并不能直接使用Callable創(chuàng)建線程,只有Runnable可以。
7、加入線程池運(yùn)行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。
