java執行緒池執行有返回值執行緒原始碼詳解
阿新 • • 發佈:2018-12-13
java執行緒池提供了幾種執行執行緒的方式,這裡主要討論關於執行有返回值的執行緒的實現原理
方式一:
使用方式:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new impleCallable());
String sss = (String) future.get();
System.out.println(sss);
public class impleCallable implements Callable{
@Override
public Object call() throws Exception {
return "我是實現了Callable的一個執行緒";
}
}
輸出:
我是實現了Callable的一個執行緒
原始碼:
下面看呼叫的submit方法
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
可以看到是把我們實現了Callabe的物件封裝成了一個FutureTask物件,並且呼叫了執行緒池的execute()方法,咦。。這個方法不是我們平常把執行緒丟到執行緒池裡執行的方法嗎?為啥我們呼叫的是執行緒池的submit()方法最終是呼叫了execute()方法呢?這個FutureTask何許人也?下面來看。
public class FutureTask<V> implements RunnableFuture<V> {
public void run() {
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
try {
result = c.call();
} catch (Throwable ex) {
}
if (ran)
set(result);
}
}
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
}
}
可以看到這個FutureTask本身也是實現Runnable介面,也就是他也是一個執行緒,看他的run方法可以看到他最終呼叫的是我們submit(T)的時候傳過來的物件的call方法,並且把返回值賦值給result.然後呼叫set方法把result複製給outcome。至此,執行緒執行完畢,並且返回值也賦值給了outcome.那麼如何獲取這個outcome呢,下面我們看FutureTask的get()方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);//等待執行緒執行完畢
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
可以看到,get方法就是把outcome給返回出去了。
方式二
使用方式
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new impleRunable(),"我是一個實現了Runnable的執行緒");
String sss = (String) future.get();
System.out.println(sss);
輸出
我是一個實現了Runnable的執行緒
原始碼
下面看呼叫的submit方法
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, 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;
}
}
可以看到與方式一不同的地方在於引數的不同,但是可以看到,最終都是把Runnable物件封裝成Callable物件的實現類RunnableAdapter,然後在call()中把我們傳進去的引數result ruturn出來。
總結:
兩種方式的背後實現其實是同一套,都是基於Callable,第二種方式會把傳進去的Runnable封裝成Callable方法,並且call中返回的是我們傳進去的result物件