Java多線程Runnable與Callable區別與拓展


我們先來分別看一下這兩個接口

 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.

 博客有很多的不足,望大家指點。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM