创建线程,可以直接extends thread类,在run方法中指定线程要执行的操作。
那还要runnable干什么呢?
因为java不支持多继承,只能继承一个类,如果继承了thread,就不能继承其他类了,这样不够灵活,不方便扩展。
而java支持实现多个接口,于是提供了一个runnable接口,接口中只有一个void run方法。
public interface Runnable {
public abstract void run();
}
通过实现runnable接口的run方法,使类具有了run的能力。再使用new thread(runnable)构造方法,把runnable送入thread执行。
//创建一个Runnable任务对象,RunnableTask实现了run方法
RunnableTask rt = new RunnableTask();
//将实现的Runnable类的实例传入thread构造函数
Thread thread=new Thread(rt);
thread.start();
实际上,thread本身也是继承了runnable接口,获取了run的能力,继承thread相当于实现了runnable。
那为什么又来了一个callable呢?
因为我们有时想要获取线程的执行结果。
thread和runnable可以通过run把任务送入线程,但是并且run方法被void修饰,没有返回值,也没提供获取执行结果的其他方法。
于是callable接口来啦,它里面只有一个V call方法。子类可以自定义call方法的返回值类型,使用return将结果返回。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
* 计算得到结果,如果无法计算就抛出异常
*/
V call() throws Exception;
}
>.<,那怎么使用callable呢,futureTask又是什么鬼?
细细观察thread构造方法,发现有传入runnable的构造方法,但是没有直接传入callable的构造方法。
那怎么办呢,callable的call方法已经写好了任务,还能返回结果,如何把callable送入线程执行呢。
答:把callable装到runnable里面,再送入thread。这里的futureTask就是一种runnable对象,用来装callable。
futureTask实现了runnable和future接口,具有run的能力,而future接口就提供了get执行结果的能力。
// 创建Callable接口任务,CallableTask里面实现了call方法
CallableTask task = new CallableTask();
// 把Callable包装成runnable,即FutureTask类
FutureTask<Integer> futureTask = new FutureTask<>(task);
//把runnable送入thread执行
new Thread(futureTask).start();
// 使用future接口提供的get方法,获取线程执行结果
System.out.println(futureTask.get());
futureTask为什么还支持传入runnable,runnable不是应该直接传给thread吗,为什么还要放入futureTask然后再传给thread?
从thread视角看,runnable不直接到碗里来,反而先变成一个callable,然后又被futureTask包起来变成一个runnable,最后再进入thread。这不是多此一举吗?
我想,应该是为了获得返回结果的能力。比如我已经使用runnable写好了run方法,它已经在很多地方使用,如何才能不修改代码也能让它获取到执行结果呢。
futureTask支持传入runnable,其内部会把runable转换为callable,这样runnable就获得了get结果的方法,然后原始runnable就伪装成callable被futureTask带进thread。
如果任务执行成功的话,就会返回传入的result。如果不需要返回值的话可以传入一个null。
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
java.util.concurrent.Executors里面提供了适配器方法把runnable转换为callable。
/**
* java.util.concurrent.Executors.java
*/
//传入Runnable和装结果的result,就能返回一个Callable
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
//真正执行转换的类,RunnableAdapter是一种Callable类
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
//runnable的run方法被包进Callable的call方法中
public T call() {
task.run();
return result;
}
}