Java中Callable實現多執行緒
一、Runnable和Callable< V >原始碼
先回顧一下Runnable和Callable< V >原始碼吧;
//Runnable 原始碼
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//Callable<V>原始碼
public interface Callable<V> {
//返回V物件
V call() throws Exception;
}
原始碼中得出基本資訊:
1.Runnable和Callable< V >的原始碼真的是少到極致了。這應該是遵循設計原則中的介面隔離原則吧,二者都是定義了一個標準的函式,具體邏則是由其子類實現;
2.Runnable 介面中宣告返回值為void的run方法;所以Runnable開啟的執行緒猶如脫繮駿馬任其馳騁難以掌控,結果導致兩個問題:1).無法直接獲取到執行緒執行執行的結果,需要藉助一個全域性變數;2)不能直接捕獲執行緒執行中的異常;
正是由於Runnable的“無法無天”難以控制,所以Java中的Callable< v >應用而生;
3.在Callable< v >原始碼可以看到與Runnable不同的是Callable< v >介面引入泛型V,在宣告的call函式返回V 並且丟擲Exception真的專門應對Runnable中的沒有返回值和捕獲異常的個問題的。
二、Callable< V >的非同步管理類Future< V >
Future< V >介面
正如上面說的與Runable相比Callable< v >開發了更多的管理操作的許可權,但是Callable中只有call函式,所以Java中提供了一個Future類用於管理Callable。然而Future< V >也同樣是一個介面:
//Future<V>原始碼
public interface Future<V> {
//取消Task執行緒:引數 如果是執行緒正在執行,是否中斷;返回中斷結果:ture 中斷成功;false 中斷失敗
boolean cancel(boolean mayInterruptIfRunning);
//判斷該Task是否已近取消
boolean isCancelled();
//判斷該Task是否完成
boolean isDone();
//獲取該執行緒返回的結果,如果執行緒為執行完阻塞,等待執行完再返回
V get() throws InterruptedException, ExecutionException;
//獲取該執行緒返回的結果,設定超時,超時丟擲Exception
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future< V >定義標準函式的介面,基本功能:
- 1.判斷當前任務是否完成
- 2.中斷當前任務,判斷是否已近中斷
- 3.阻塞或者設定超時,獲取非同步Task結果,
Future< V >的實現類FutureTask
在原始碼中FutureTask實現RunnableFuture< V > 介面,然而RunnableFuture< V > 同樣是實現了Runnable和Future< V > 介面;按照Java的繼承原則和特徵, 因此FutureTask實際中實現了Future< V >介面,同時也具備了Future的所用基本功能;
三、開啟Callable< V >非同步執行緒的基本方式
首先在Java中開啟非同步執行緒都應該會想到執行緒池吧,Callable同樣可以藉助執行緒池進行開啟關閉,其次Callable< V >非同步執行緒的開啟肯定是要藉助它的實行類FutureTask,上文中已經提到的FutureTask是implements了Runnable,所以FutureTask同樣可以藉助Thread類包裝直接執行;
1.執行緒池中的在ExecutorService中定義了< T > Future< T > submit(Callable< T > task)函式就是用於開始執行Callable< V >任務滴;
ExecutorService開啟Callable的demo程式碼:
public static void main(String[] args) {
//執行緒池submit開啟多執行緒
try {
CallableMThread mThread = new CallableMThread();
ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Future<String> submit = threadPool.submit(mThread);
String resualt = submit.get();
System.out.println(resualt);
threadPool.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//內部類的實現Callable<V>開啟新執行緒方式
class CallableMThread implements Callable<String> {
@Override
public String call() throws Exception {
// TO DO
return "ThreadID :" + Thread.currentThread().getId() + " ThreadRandom :" + (Math.random() * 1000);
}
}
2.藉助Thread包裝開啟Callable< V >非同步執行緒
實現Callable< T >介面多執行緒例子
public static void main(String[] args) {
try {
//FutureTask啟動執行緒
CallableMThread mThread = new CallableMThread();
FutureTask<String> task = new FutureTask<String>(mThread);
new Thread(task).start();
//返回的String結果
String resualt = task.get();
System.out.println(resualt);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//內部類的實現Callable<V>開啟新執行緒方式
class CallableMThread implements Callable<String> {
@Override
public String call() throws Exception {
// TO DO
return "ThreadID :" + Thread.currentThread().getId() + " ThreadRandom :" + (Math.random() * 1000);
}
}
}
3.Callable< V >開啟一組執行緒;
實際開發中有些特殊場景需求是最短時間內完協作成幾個Task,最後彙總結果呈現給使用者;這一類需求往往是十分重要的互動介面的需求,所以咋能不來個demo呢
public static void main(String[] args) {
try {
CallableMThread mThread = new CallableMThread();
int coreSize = Runtime.getRuntime().availableProcessors();
//建立一個執行緒
ExecutorService executor = Executors.newFixedThreadPool(coreSize);
//建立多個有返回值的任務
List<Future> list = new LinkedList<Future>();
for (int i = 0; i < coreSize; i++) {
FutureTask task = new FutureTask(mThread);
//執行任務並獲取future
Future future = executor.submit(task);
list.add(future);
}
//關閉執行緒池
executor.shutdown();
//獲取併發任務結果
for (Future mFuture : list) {
//Future物件獲取返回值
String resualt = mFuture.get().toString();
//輸出結果
System.out.printf(resualt);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//內部類的實現Callable<V>開啟新執行緒方式
class CallableMThread implements Callable<String> {
@Override
public String call() throws Exception {
// TO DO
return "ThreadID :" + Thread.currentThread().getId() + " ThreadRandom :" + (Math.random() * 1000);
}
}