前言
本文旨在简单讲解
Runnable
、
Callable
、
FutureTask
这几个线程执行相关的接口和类。为后面FutureTask源码讲解作铺垫。
JUC框架 系列文章目录
Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
我们知道创建线程有两种方式:
- override掉
的run方法:Thread
new Thread() {
@Override
public void run() {
int count = 0;
for(int i = 1;i <= 100;i++)
count += i;
}
}.start();
看过
Thread
源码都知道,我们调用
Thread#start()
后,会创建一个新线程来执行这个
Thread
的run方法。但上面这种执行者和执行task绑定在一起了,不灵活。
- 传递一个
对象给Runnable
:Thread
new Thread(new Runnable(){
@Override
public void run() {
int count = 0;
for(int i = 1;i <= 100;i++)
count += i;
}
}).start();
这样,通过创建一个
Runnable
匿名内部类对象,可以达到同样的效果,但是却把执行者和执行task分开了。
public class Thread implements Runnable {
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
从
Thread
源码可以看到,当没有override掉run方法时,run方法将执行持有的
Runnable
对象的run方法。简单的说,就是套娃。
public class test5 {
public static Runnable task = new Runnable() {
@Override
public void run() {
int count = 0;
for(int i = 1;i <= 100;i++)
count += i;
}
};
public static void main(String[] args) throws InterruptedException {
new Thread(task).start();
new Thread(task).start();
}
}
将执行者和执行task分开是有好处,上例就体现了两个执行者可以执行同一个task。
Callable
与Runnable相比,Callable接口有些不同之处:
- Runnable接口没有返回值,Callable接口有返回值。又因为是返回值是泛型,所以任何类型的返回值都支持。
- Runnable接口的run方法没有
。这意味着,Runnable不能抛出异常(子类不能抛出比父类更多的异常,但现在Runnable的run方法本身没有抛出任何异常);Callable接口可以抛出异常。throws Exception
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
但注意,
Thread
并没有一个构造器可以接受
Callable
参数的,而且
Thread
也没有一个
Callable
类型成员的。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
看来要想使用
Callable
还得依靠别的东西。
FutureTask
先回答上面的问题,要想使用
Callable
还得依靠
FutureTask
,虽然这里暂且看不出来
FutureTask
和
Callable
的关系。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
原来是
FutureTask
的构造器可以接受一个
Callable
对象,这就把这二者串起来了,而
FutureTask
本身又是一个
Runnable
,这说明可以把
FutureTask
传递给
Thread
对象的构造器。
public class test5 {
public static void main(String[] args) throws InterruptedException {
FutureTask<Integer> result = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int count = 0;
for(int i = 1;i <= 100;i++)
count += i;
return count;
}
});
new Thread(result).start();
try {
System.out.println(result.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
/*output:
5050
*/
上面例子给出了
FutureTask
的用法,看起来是线程通过
FutureTask
对象间接调用到了
Callable
的call方法。注意,调用
result.get()
时主线程会阻塞一会直到call方法执行完毕。
别看这个类图稍微复杂,其实
RunnableFuture
就是将
Future
和
Runnable
合成一个新接口而已,但没有增加任何一个新方法。
Runnable
我们已经看过了,看看
Future
吧:
public interface Future<V> {
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
}
Future被设计为一个可以异步获得task执行结果的接口。
-
。获得task执行结果,如果task已经执行完,马上返回执行结果;如果task未执行完毕,则阻塞直到执行完毕。get()
-
。上一个函数的超时版本,阻塞直到 执行完毕或超时。get(long timeout, TimeUnit unit)
-
。尝试取消task,返回值代表取消操作成功。cancel(boolean mayInterruptIfRunning)
-
。判断一个task已经被取消了。取消一定是task没有执行完毕前就被取消,也包括根本没有执行就被取消。isCancelled()
-
。如果一个任务已经结束, 则返回true。返回true有三种情况:isDone()
-
。正常执行完毕。normal termination
-
。执行过程抛出异常。an exception
-
。task被取消。cancellation
-
当然,一般我们是配合线程池来使用
Callable
:
package com.xxx.future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NoFuture {
public static class Tasker implements Runnable{
@Override
public void run(){
System.out.println("ah...");
}
}
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
for(int i=0;i<5;i++){
service.submit(new Tasker());
}
service.shutdown();
}
}
但其实线程池的做法也是构造一个
FutureTask
。
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}