Runnable
public interface Runnable {
public abstract void run();
}
Runnable的代碼非常簡單,它是一個接口且只有一個run(),創建一個類實現它,把一些費時操作寫在其中,然后使用某個線程去執行該Runnable實現類即可實現多線程。
Callable
public interface Callable<V> {
V call() throws Exception;
}
Callable的代碼也非常簡單,不同的是它是一個泛型接口,call()函數返回的類型就是創建Callable傳進來的V類型。
學習Callable對比著Runnable,這樣就很快能理解它。Callable與Runnable的功能大致相似,Callable功能強大一些,就是被線程執行后,可以返回值,并且能拋出異常。
Future
Future實現代碼
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future是一個接口,定義了Future對于具體的Runnable或者Callable任務的執行結果進行取消、查詢任務是否被取消,查詢是否完成、獲取結果。
Future基本用法:
class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("做一些耗時的任務...");
Thread.sleep(5000);
return "OK";
}
}
public class FutureSimpleDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new MyCallable());
System.out.println("dosomething...");
System.out.println("得到異步任務返回結果:" + future.get());
System.out.println("Completed!");
}
}
上面是Future基本用法的代碼以及并運行,我們可以知道:
- 線程是屬于異步計算模型,所以你不可能直接從別的線程中得到方法返回值。 這時候,Future就出場了。
- Futrue可以監視目標線程調用call的情況,當你調用Future的get()方法以獲得結果時,當前線程就開始阻塞,直接call方法結束返回結果。
- Future引用對象指向的實際是FutureTask。
也就是說,總結一句話,Future可以得到別的線程任務方法的返回值。
FutureTask
FutureTask繼承結構
FutureTask的父類是RunnableFuture,而RunnableFuture繼承了Runnbale和Futrue這兩個接口
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
FutureTask構造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
在這里我們可以了解到:
- FutureTask最終都是執行Callable類型的任務。
- 如果構造函數參數是Runnable,會被Executors.callable方法轉換為Callable類型。
接下來我們看看Executors.callable方法代碼
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
代碼很簡單,直接返回一個RunnableAdapter實例。
接下來我們看看RunnableAdapter方法代碼
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
可以了解到:
- RunnableAdapter是FutureTask的一個靜態內部類并且實現了Callable,也就是說RunnableAdapter是Callable子類。
- call方法實現代碼是,執行Runnable的run方法,并返回構造FutureTask傳入result參數。
FutureTask基本用法
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(3000);// 可能做一些事情
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
FutureTask總結
FutureTask實現了兩個接口,Runnable和Future,所以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的返回值,那么這個組合的使用有什么好處呢?假設有一個很費時邏輯需要計算并且返回這個值,同時這個值不是馬上需要,那么就可以使用這個組合,用另一個線程去計算返回值,而當前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到!
注意:
通過Executor執行線程任務都是以Callable形式,如果傳入Runnable都會轉化為Callable。
通過new Thread(runnable),只能是Runnable子類形式。