我們先來分別看一下這兩個接口
Runnable:
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package java.lang; 7 8 @FunctionalInterface 9 public interface Runnable { 10 void run(); 11 }
只有一個方法run(),表示執行任務的邏輯。
Callable:
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package java.util.concurrent; 7 8 @FunctionalInterface 9 public interface Callable<V> { 10 V call() throws Exception; 11 }
也只有一個方法call(),但是是一個有返回值的方法,這給我們提供了獲取方法執行結果的可能,即使它是個異步的任務,它可以獲取異常,給我們極大地便利知道任務執行失敗的原因。
Runnable例子Demo:
1 package cn.concurrent.executor; 2 3 /** 4 * Created by spark on 17-9-24. 5 */ 6 public class RunnabelDemo implements Runnable { 7 8 public RunnabelDemo(String acceptStr) { 9 this.acceptStr = acceptStr; 10 } 11 12 private String acceptStr; 13 14 @Override 15 public void run() { 16 try { 17 // 線程阻塞 1 秒,此時有異常產生,只能在方法內部消化,無法上拋 18 Thread.sleep(1000); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 // 最終處理結果無法返回 23 System.out.println("hello : " + this.acceptStr); 24 } 25 26 27 public static void main(String[] args) { 28 Runnable runnable = new RunnabelDemo("my runable test!"); 29 long beginTime = System.currentTimeMillis(); 30 new Thread(runnable).start(); 31 long endTime = System.currentTimeMillis(); 32 System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!"); 33 } 34 }
結果:
1 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=41745:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.RunnabelDemo 2 cast : 0 second! 3 hello : my runable test! 4 5 Process finished with exit code 0
Callable的例子:
1 package cn.concurrent.executor; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.Future; 6 import java.util.concurrent.FutureTask; 7 8 /** 9 * Created by spark on 17-9-24. 10 */ 11 public class CallableDemo implements Callable<String> { 12 13 private String name; 14 15 public CallableDemo(String name) { 16 this.name = name; 17 } 18 19 @Override 20 public String call() throws Exception { 21 //執行任務的相關邏輯 22 Thread.sleep(1000); 23 return name + " : this is task is successed."; 24 } 25 26 public static void main(String[] args) throws ExecutionException, InterruptedException { 27 CallableDemo callableDemo = new CallableDemo("wade"); 28 //異步執行的結果 29 FutureTask<String> future = new FutureTask<String>(callableDemo); 30 new Thread(future).start(); 31 //調用get()方法--阻塞 32 String result=future.get(); 33 System.out.println("hello : " + result); 34 } 35 }
結果:
1 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=37879:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.CallableDemo 2 hello : wade : this is task is successed. 3 4 Process finished with exit code 0
問題:如何把一個Runnbale的任務轉變為有返回值的Callable的任務。
^_^我們有工具類Executors可以幫我們實現,如下圖:
其中Executors.callable(Runnable),就是我們需要的方法,是不是很簡單呢。
代碼如下:
1 package cn.concurrent.executor; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.Executors; 6 import java.util.concurrent.FutureTask; 7 8 /** 9 * Created by spark on 17-9-24. 10 */ 11 public class RunnableToCallable implements Runnable { 12 13 String name; 14 15 public RunnableToCallable(String name) { 16 this.name = name; 17 } 18 19 @Override 20 public void run() { 21 try { 22 Thread.sleep(1000); 23 System.out.println(name + " :任務執行完畢!"); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 public static void main(String[] args) throws ExecutionException, InterruptedException { 30 RunnableToCallable task=new RunnableToCallable("wade"); 31 //轉變為有返回值的任務 32 Callable<Object> future=Executors.callable(task); 33 FutureTask<Object> futureTask=new FutureTask<Object>(future); 34 new Thread(futureTask).start(); 35 //調用get()方法--阻塞 36 Object result=futureTask.get(); 37 System.out.println("hello : " + result); 38 } 39 }
結果如下:
1 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=36136:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.RunnableToCallable 2 wade :任務執行完畢! 3 hello : null 4 5 Process finished with exit code 0
說明:因為該任務沒有返回結果,因此返回的為null.
接下來我們討論一下ExecutorService.executr()與ExecutorService.submit()方法的區別。
類圖如下:
從類圖可以看到:
execute只能接受Runnable類型的任務,並且沒有返回值。
submit不管是Runnable還是Callable類型的任務都可以接受,但是Runnable返回值均為void,所以使用Future的get()獲得的還是null
然后來看異常處理:
對於Runnable 只能通過try-catch來捕獲異常,對於Callable,直接拋出就可以,然后在get的時候捕獲異常進行處理。
如下:
1 package cn.concurrent.executor; 2 3 import java.util.concurrent.*; 4 5 /** 6 * Created by spark on 17-9-24. 7 */ 8 public class ThreadExceptionTest { 9 10 11 public static void main(String[] args) { 12 ExecutorService executorService = Executors.newCachedThreadPool(); 13 CallableTest callableTest = new CallableTest(); 14 Future<Boolean> task = executorService.submit(callableTest); 15 try { 16 Boolean b = task.get(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } catch (ExecutionException e) { 20 e.printStackTrace(); 21 } 22 23 } 24 25 static class CallableTest implements Callable<Boolean> { 26 27 @Override 28 public Boolean call() throws Exception { 29 int num = 3 / 0; 30 return false; 31 } 32 } 33 }
結果:
1 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=42960:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.ThreadExceptionTest 2 java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero 3 at java.util.concurrent.FutureTask.report(FutureTask.java:122) 4 at java.util.concurrent.FutureTask.get(FutureTask.java:192) 5 at cn.concurrent.executor.ThreadExceptionTest.main(ThreadExceptionTest.java:16) 6 Caused by: java.lang.ArithmeticException: / by zero 7 at cn.concurrent.executor.ThreadExceptionTest$CallableTest.call(ThreadExceptionTest.java:29) 8 at cn.concurrent.executor.ThreadExceptionTest$CallableTest.call(ThreadExceptionTest.java:25) 9 at java.util.concurrent.FutureTask.run(FutureTask.java:266) 10 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 11 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 12 at java.lang.Thread.run(Thread.java:748) 13 14 Process finished with exit code 0
get()方法會捕獲異常。
接下來,我們詳細了解一下Future接口,實現類FutureTask(它有三種狀態,分別為未啟動,已啟動,已完成);
類圖如下:
1.首先可以看到,它也是一個線程,可以直接繼承它來運行任務,然后交給ExecutorService執行。
如下:
1 package cn.concurrent.executor; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.FutureTask; 6 7 /** 8 * Created by spark on 17-9-24. 9 */ 10 public class FutureTaskDemo extends FutureTask<String> { 11 public FutureTaskDemo(Runnable runnable, String s) { 12 super(runnable, s); 13 System.out.println(s); 14 } 15 16 @Override 17 public void run() { 18 try { 19 Thread.sleep(1000); 20 System.out.println("this is running."); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 26 public static void main(String[] args) { 27 FutureTaskDemo futureTaskDemo = new FutureTaskDemo(new Runnable() { 28 @Override 29 public void run() { 30 System.out.println("this is wade."); 31 } 32 }, "wade"); 33 34 ExecutorService es = Executors.newFixedThreadPool(1); 35 FutureTask<String> task = (FutureTask<String>) es.submit(futureTaskDemo); 36 System.out.println("hello:"+task); 37 es.shutdown(); 38 } 39 }
結果如下:
1 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -javaagent:/usr/local/idea/lib/idea_rt.jar=46018:/usr/local/idea/bin -Dfile.encoding=UTF-8 -classpath /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/cldrdata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/dnsns.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/icedtea-sound.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/jaccess.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/localedata.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/nashorn.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunec.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunjce_provider.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/sunpkcs11.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/ext/zipfs.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/management-agent.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/rt.jar:/home/spark/IdeaProjects/thread/thread/target/classes:/home/spark/.m2/repository/com/lmax/disruptor/3.3.2/disruptor-3.3.2.jar cn.concurrent.executor.FutureTaskDemo 2 wade 3 hello:java.util.concurrent.FutureTask@266474c2 4 this is running. 5 6 Process finished with exit code 0
2.看一下run()方法
1 public void run() { 2 /** 3 *首先判斷任務的狀態 如果任務的狀態不是new 說明任務的狀態已經改變(說明他已經走了4種可能變化的一種) 4 * 如果狀態是new就會把 當前執行任務的線程付給runner, 這里用的cmpandset如果runner不為空 說明已經有線程在執行 5 * 任務也會退出執行,如果狀態是new並且runner為空並且把當前的線程付給了runner那么就繼續執行任務(runner state 都是 volatile 6 *類型的變量是一個很輕量機的線程安全操作) 7 *引起state狀態變化的原因 就是調用了cancel 或是 run 8 **/ 9 if (state != NEW || 10 !UNSAFE.compareAndSwapObject(this, runnerOffset, 11 null, Thread.currentThread())) 12 return; 13 14 //開始執行任務 15 try { 16 Callable<V> c = callable; 17 /** 18 * 如果 要執行的任務不為空 並且狀態 new 就執行 19 20 ***/ 21 if (c != null && state == NEW) { 22 V result; 23 boolean ran; 24 try { 25 //執行任務 26 result = c.call(); 27 //如果沒有意外發生就執行成功了 28 ran = true; 29 } catch (Throwable ex) { 30 //有異常 31 result = null; 32 ran = false; 33 //設置異常 34 setException(ex); 35 } 36 //如果轉型成功了 設置結果 37 if (ran) 38 (result); 39 } 40 } finally { 41 // runner must be non-null until state is settled to 42 // prevent concurrent calls to run() 43 //不管是否執行成功了 都把runner設置成null 44 runner = null; 45 // state must be re-read after nulling runner to prevent 46 // leaked interrupts 47 int s = state; 48 if (s >= INTERRUPTING) 49 handlePossibleCancellationInterrupt(s); 50 } 51 }
Task執行后如果成功會調用set()方法,如果有異常會調用setException()方法。
我們先看下set方法 :
1 protected void set(V v) { 2 /** 3 *如過state是new 把state設置成 COMPLETING 4 * 5 **/ 6 7 if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { 8 outcome = v; 9 //將任務設置成NORMAL over the task 10 UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state 11 finishCompletion(); 12 } 13 }
最后,來看一下最重要的方法get();
public V get() throws InterruptedException, ExecutionException {
int s = state;
//當線程狀態為新建活着執行中時一直調用awaitDone方法 if (s <= COMPLETING)
//循環判斷線程狀態是否已經執行成功,如果執行成功返回線程狀態;其中還包括線程取消,中斷等情況的判斷。可參見下方源碼。
//所以這里便是上面例子中為什么線程執行成功后即可立即得到結果,如果還沒有執行成功
s = awaitDone(false, 0L);
//線程狀態正常返回結果
return report(s);
}
如果get時,FutureTask的狀態為未完成狀態,則調用awaitDone方法進行阻塞。awaitDone():
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//計算一下需要等待的時間,有可能為0,為0的話就無限期等待。
final long deadline = timed ? System.nanoTime() + nanos : 0L;
//一個等待節點
WaitNode q = null;
//是否加入隊列
boolean queued = false;
for (;;) {
//如果當且調用get方法的線程被interrupt 那么就把當前線程從等待隊列remove
//然后拋出異常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//如果任務已經完成 不管是被暫停了 還是出現異常了 只要狀態大於COMPLETING就返回。
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
//如果任務正在完成中(注意是ING進行時) 那么讓出一會CPU時間在繼續執行
Thread.yield();
else if (q == null)
//創建等待節點
q = new WaitNode();
else if (!queued)
//第一次創建的等待節點需要加入等待隊列,這里加入隊列等待。
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
//如果設置了等待 那么就得鎖住線程等待,如果時間到了就返回狀態。
//方法 public V get(long timeout, TimeUnit unit) 這里會根據狀態做后續處理。
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
//否則鎖住線程等待其他線程解鎖。
LockSupport.park(this);
}
}
awaitDone方法可以看成是不斷輪詢查看FutureTask的狀態。在get阻塞期間:
-
如果執行get的線程被中斷,則移除FutureTask的所有阻塞隊列中的線程(waiters),並拋出中斷異常;
-
如果FutureTask的狀態轉換為完成狀態(正常完成或取消),則返回完成狀態;
-
如果FutureTask的狀態變為COMPLETING, 則說明正在set結果,此時讓線程等一等;
-
如果FutureTask的狀態為初始態NEW,則將當前線程加入到FutureTask的阻塞線程中去;
-
如果get方法沒有設置超時時間,則阻塞當前調用get線程;如果設置了超時時間,則判斷是否達到超時時間,如果到達,則移除FutureTask的所有阻塞列隊中的線程,並返回此時FutureTask的狀態,如果未到達時間,則在剩下的時間內繼續阻塞當前線程。
詳細了解這幾個方法:
當FutureTask處於未啟動或者已啟動狀態時,執行Future.get()方法,將導致調用線程阻塞,當FutureTask處於已完成狀態時,執行Future.get()方法時,將調用線程立即返回結果或者拋出異常。
當FutureTask處於未啟動狀態時,執行Future.cancel()方法,導致此任務永遠不會被執行 ;當FutureTask處於已啟動狀態時,執行Future.cancel()方法將以中斷執行此任務線程的方式來試圖停止任務;當FutureTask處於已啟動狀態時,執行Future.cancel(false)方法不會對正在執行此任務的線程產生影響。
說明:Future.get()方法的實現基於AbstractQueuedSynchronizer(簡稱為AQS),並發包中有很多可阻塞類(如ReentrantLock)都是基於AQS實現的,AQS是一個同步框架,它提供了通用機制來原子性管理同步狀態,阻塞和喚醒線程,以及維護被阻塞線程的隊列,后面有專門博客介紹AQS.
博客有很多的不足,望大家指點。