[Java併發程式設計實戰] Future+callable+FutureTask 閉鎖機制(含示例程式碼)
業精於勤,荒於嬉;行成於思,毀於隨。—韓愈
它告訴我們,事業的成功在於奮發努力,勤勉進取,太貪玩,放鬆要求便會一事無成;做事情要想成功,需要反覆思考、深思熟慮,而隨手隨意、隨隨便便行事,做事不經過大腦,必然招致失敗。
FutureTask 也可以做閉鎖,它是 Future 和 callable 的結合體。所以我們有必要來了解 FutureTask 這個類。
FutureTask 的繼承關係類圖
先看 FutureTask 類的繼承:
public class FutureTask<V> implements RunnableFuture<V>
它繼承自 RunnableFuture,可以看出他是 Runnable 和 Future 的結合體。
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
我們熟悉的 Runnable 介面:
public interface Runnable {
public abstract void run();
}
不常見的Future 介面,用來獲取非同步計算結果:
public interface Future<V> {
/**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*/
boolean isCancelled();//如果任務被取消,返回true
/**
* Returns {@code true} if this task completed.
*/
boolean isDone();//如果任務執行結束,無論是正常結束或是中途取消還是發生異常,都返回true。
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*/
V get() throws InterruptedException, ExecutionException; //獲取非同步執行的結果,如果沒有結果可用,此方法會阻塞直到非同步計算完成。
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
到這裡,FutureTask 整個繼承關係已經很清楚了。為了更直觀一點,我用 starUML 畫出它的類繼承關係圖。
在類關係圖中,我們可以看到 FutureTask 的建構函式,包含了之前沒有見過的型別:Callable。我們直接看下它的兩個建構函式實現,進一步瞭解看看:
//建構函式1
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//建構函式2
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
這裡已經非常清楚了,最終都是賦值給 FutureTask 的內部變數 callable。它是一個介面,包含一個有返回值的函式 call()。
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;
}
通過上面的講解,我們已經知道 Future,FutureTask,Callable,Runnable的關係了。那麼,說了這麼多主要是想幹嘛呢?
沒錯,主要就是為了執行緒執行完成後能夠返回結果。我們知道,Runnable 介面執行完成後,是沒法返回結果的。所以,我們如果想要能夠返回執行的結果,必須使用 callable 介面。
應用場景
比如我們有個耗時的計算操作,現在建立一個子執行緒執行計算操作,主執行緒通過 FutureTask.get() 的方式獲取計算結果,如果計算還沒有完成,則會阻塞一直等到計算完成。
下面我們直接編寫程式碼來實現上面的應用場景。
使用 Callable + FutureTask 獲取執行結果:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTaskTest{
//建立一個Future物件,並把Callable的實現傳給建構函式
private static final FutureTask<Integer> future = new FutureTask<Integer>(new CallableTest());
public static void main(String[] args) {
//建立一個執行緒
final Thread thread = new Thread(future);
//啟動執行緒
thread.start();
try {
Thread.sleep(1000);
System.out.println("Main thread is running");
//獲取計算結果,會阻塞知道計算完畢
System.out.println("get the sub thread compute result : " + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main thread is end");
}
//實現Callable介面,耗時操作
static class CallableTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int ret = 0;
Thread.sleep(1000);
System.out.println("sub thread is computing");
for(int i = 0; i < 1000; i++) {
ret += i;
}
System.out.println("sub thread is finish compute");
return ret;
}
}
}
執行結果:
另外一種方式,是使用 Callable + Future + ExecutorService 的方式。ExecutorService繼承自Executor,它的目的是為我們管理Thread物件,從而簡化併發程式設計,Executor使我們無需顯示的去管理執行緒的生命週期。
在ExecutorService介面中聲明瞭若干個submit方法的過載版本:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一個submit方法裡面的引數型別就是Callable。
示例如下:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class FutureTaskTest{
public static void main(String[] args) {
//返回一個執行緒池,通常都和這種執行緒寬架搭配
ExecutorService threadPool = Executors.newSingleThreadExecutor();
System.out.println("Main thread is running");
//提交給執行緒,返回一個Future類,並執行
Future<Integer> future = threadPool.submit(new CallableTest());
try {
Thread.sleep(1000);
//獲取計算結果,會阻塞知道計算完畢
System.out.println("get the sub thread compute result : " + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main thread is end");
}
//實現Callable介面,耗時操作
static class CallableTest implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int ret = 0;
Thread.sleep(1000);
System.out.println("sub thread is computing");
for(int i = 0; i < 1000; i++) {
ret += i;
}
System.out.println("sub thread is finish compute");
return ret;
}
}
}
執行結果
本文完結,希望看完對你有幫助,歡迎關注我~
本文原創首發於微信公眾號 [ 林裡少年 ],歡迎關注第一時間獲取更新。