Java多线程Runnable与Callable区别与拓展
Posted 霓羽决奕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.
博客有很多的不足,望大家指点。
以上是关于Java多线程Runnable与Callable区别与拓展的主要内容,如果未能解决你的问题,请参考以下文章
java 创建线程的三种方法Callable,Runnable,Thread比较及用法