zoukankan      html  css  js  c++  java
  • 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.

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

  • 相关阅读:
    增加新分类daily——“每天学了啥?”
    gcc选项中的编译过程
    一个带路径复制的perl脚本
    git
    mysql explain 详解
    CentOS Python2.4升级到Python2.7
    mongoDb查询
    linux php 扩展
    php-redis 扩展安装
    redis Linux的安装方法
  • 原文地址:https://www.cnblogs.com/lwy19998273333/p/7589016.html
Copyright © 2011-2022 走看看