Callable介面和Future介面 創建線程的方式 1.繼承Thread類2.實現Runnable介面3.Callable介面4.線程池方式 Callable介面 在繼承Thread類和實現Runnable介面的方式創建線程時,線程執行的run方法中,返回值是void,即無法返回線程的執行結果, ...
Callable介面和Future介面
創建線程的方式
1.繼承Thread類2.實現Runnable介面3.Callable介面4.線程池方式
Callable介面
在繼承Thread類和實現Runnable介面的方式創建線程時,線程執行的run方法中,返回值是void,即無法返回線程的執行結果,為了支持該功能,java提供了Callable介面。
Callable和Runnable介面的區別
- 1.Callable中的call()方法,可以返回值,Runnable介面中的run方法,無返回值(void)。
- 2.call()方法可以拋出異常,run()不能拋異常。
- 3.實現Callable介面,必須重寫call()方法,run是抽象方法,抽象類可以不實現。
- 4.不能用Callable介面的對象,直接替換Runnable介面對象,創建線程。
Future介面
當call()方法完成時,結果必須存儲在主線程已知的對象中,以便主線程可以知道線程返回的結果,可以使用Future對象。將Future視為保存結果的對象-該對象,不一定會將call介面返回值,保存到主線程中,但是會持有call返回值。Future基本上是主線程可以跟蹤以及其他線程的結果的一種方式。要實現此介面,必須重寫5個方法。
public interface Future<V> {//Future源碼
/**
* 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.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning);
//用於停止任務,如果未啟動,將停止任務,已啟動,僅在mayInterrupt為true時才會中斷任務
/**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled();
/**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone();//判斷任務是否完成,完成true,未完成false
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
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.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Callable和Future是分別做各自的事情,Callable和Runnable類似,因為它封裝了要在另一個線程上允許的任務,而Future用於存儲從另一個線程獲取的結果。實際上,Future和Runnable可以一起使用。創建線程需要Runnable,為了獲取結果,需要Future。
FutureTask
Java庫本身提供了具體的FutureTask類,該類實現了Runnable和Future介面,並方便的將兩種功能組合在一起。並且其構造函數提供了Callable來創建FutureTask對象。然後將該FutureTask對象提供給Thread的構造函數以創建Thread對象。因此,間接的使用Callable創建線程。
關於FutureTask構造函數的理解
public class FutureTask<V> implements RunnableFuture<V> {//實現了RunnableFuture
//該類的構造函數有兩個
public FutureTask(Callable<V> callable) {//
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//用此方法創建的線程,最後start()調用的其實是Executors.RunnableAdapter類的call();
public FutureTask(Runnable runnable, V result) {//返回的結果,就是構造函數傳入的值
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//原因如下
//使用FutureTask對象的方式,創建的Thread,在開啟線程.start後,底層執行的是
//Thread的方法 start()方法會調用start0(),但是調用這個start0()(操作系統創建線程)的時機,是操作系統決定的,但是這個start0()最後會調用run()方法,此線程的執行內容就在傳入的對象的run()方法中
public void run() {
if (target != null) {
target.run();
//執行到這 target就是在創建Thread時,傳遞的Runnable介面的子類對象,FUtureTask方式就是傳入的FutureTask對象
//會執行FutureTask中的run()方法
}
}
//FutureTask的run()
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;//類的屬性
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
//會執行callable的call(),callable是創建TutureTask時,根據傳入的Runnable的對象封裝後的一個對象
//this.callable = Executors.callable(runnable, result);
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
//Executors類的callable
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
//最後執行到這裡 Executors類的靜態內部類RunnableAdapter
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;
}
}
}
public interface RunnableFuture<V> extends Runnable, Future<V> {//繼承了Runnable, Future<V>介面
void run();//
}
使用FutureTask類間接的用Callable介面創建線程
/**
* @author 長名06
* @version 1.0
* 演示Callable創建線程 需要使用到Runnable的實現類FutureTask類
*/
public class CallableDemo {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
System.out.println(Thread.currentThread().getName() + "使用Callable介面創建的線程");
return 100;
});
new Thread(futureTask,"線程1").start();
}
}
核心原理
在主線程中需要執行耗時的操作時,但不想阻塞主線程,可以把這些作業交給Future對象來做。
- 1.主線程需要,可以通過Future對象獲取後臺作業的計算結果或者執行狀態。
- 2.一般FutureTask多用於耗時的計算,主線程可以在完成自己的任務後,再獲取結果。
- 3.只有執行完後,才能獲取結果,否則會阻塞get()方法。
- 4.一旦執行完後,不能重新開始或取消計算。get()只會計算一次
小結
- 1.在主線程需要執行比較耗時的操作時,但又不想阻塞主線程,可以把這些作業交給Future對象在後臺完成,當主線程將來需要時,就可以通過Future對象獲得後臺作業的計算結果或者執行狀態。
- 2.一般FutureTask多用於耗時的計算,主線程可以在完成自己的任務後,再去獲取結果。
- 3.get()方法只能獲取結果,並且只能計算完後獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或拋出異常。且只計算一次,計算完成不能重新開始或取消計算。
只是為了記錄自己的學習歷程,且本人水平有限,不對之處,請指正。