本文主要是为了介绍多线程中使用的几种任务:Runnable、Callable、FutureTask等,是对前面多线程系列的最后一个补充了,接下来两篇就是相当于实战练习了。
Runnable 和 Callable的区别
Runnable和Callable都是定义了接口,可以用在线程池中异步执行,区别是:
- Runnable可以直接被Thread执行,但是没有返回值
- Callable执行之后有返回值,但是只能提交给线程池执行。
Future 和 FutureTask
Future是一个接口,主要是线程池中任务执行之后用于返回结果的获取,定义了
- boolean cancel(boolean mayInterruptIfRunning); 取消任务
- boolean isCancelled(); 任务是否取消
- boolean isDone(); 任务是否执行完毕
- V get(); 获取任务执行的结果,注意这个方法阻塞线程
- V get(long timeout, TimeUnit unit); 同上,只是增加了一个超时时间
Future有一个直接继承接口RunnableFuture,RunnableFuture有一个实现的子类FutureTask,RunnableFuture这个接口同时还继承了Runnable接口,这意味着FutureTask可以作为Future或者Runnable使用。
再来看一下FutureTask的实现,最终内部保存了一个Callable对象,也就是提交的任务
先看构造函数
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
...
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future> f = new FutureTask(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
一共2个构造函数,一个是接受Callable,一个是接受Runnable和默认返回值。
详细看一下第二个构造参数,注释很清楚的说明,当你需要runnable可取消同时不关心返回值时,可以这样构建
Future> f = new FutureTask(runnable, null);
同时构造函数里,Runnable被适配成了一个Callable,看一下里面的实现:
/**
* Returns a {@link Callable} object that, when
* called, runs the given task and returns the given result. This
* can be useful when applying methods requiring a
* {@code Callable} to an otherwise resultless action.
* @param task the task to run
* @param result the result to return
* @param the type of the result
* @return a callable object
* @throws NullPointerException if task null
*/
public static Callable callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter(task, result);
}
...
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter implements Callable {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
上面两个函数将一个Runnable适配成了一个Callable,是Executors中提供的静态方法。
再看一下FutureTask对Runnable的实现
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
抛开其他的判断条件,其实就是对内部保存的Callable调用了call方法,进行执行并保存结果。这就是FutureTask主要的几个方法,下面有用。
ExecutorService中Future的应用
上面2点主要是为了给这点做伏笔,现在我们来看为什么ExecutorService中的submit()既可以提交Runnable又可以提交Callable并返回结果,同时看看直接execute() Runnable会有什么不同。
Future submit(Runnable task)
先来看一下这个方法的实现
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
代码上可以很直观的看到,提交的Runnable被newTaskFor()适配成了RunnableFuture。来看一下newTaskFor()这个方法的实现。
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
直接是new了一个FutureTask对象,上面我们分析过这种情况,runnable其实是会被适配成一个Callable的。
Future submit(Callable task)
再来看一下这个方法
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
跟上面的代码简直一摸一样,都是适配成了RunnableFuture。
看到这里可以明白,提交Runnable时是将Runnable适配成了Callable,也就是submit方法最终都会调用的的是Callable对象。
上面我们说过RunnableFuture实现了Runnable接口,当他被execute时,肯定是被当作Runnable使用的,看一下两个submit方法最终都是通过execute来执行的。
上面介绍FutureTask时我们知道,对Runnable的实现FutureTask最后调用的是Callable的call方法。
到这里可以知道了,
- 当我们提交一个Runnable的任务时,首先通过FutureTask的构造函数被适配成了一个Callable对象被保存FutureTask中。
- 当任务被执行时,FutureTask又被当作一个Runnable使用,调用了保存在内部的Callable的call方法,任务被执行并返回了结果。
- Runnable被适配成Callable时最终调用的还是自己的run方法。
ExecutorService中execute()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
上面注释的意思是:
- 当前核心线程数少于corePoolSize是,尝试直接新建Thread用来执行任务。同时校验添加的过程,防止出错。
- 任务入队时二次校验是否需要新建线程,判断是否需要回滚等。
- 如果任务不能入队则新建非核心线程处理,如果失败那么就拒绝任务。
这个就是任务具体执行的过程,同时也可以知道为什么上一篇博客中通过反射获取workers的size就能知道当前线程的数量。
总结
又到总结时间,博文主要讲了几个概念Runnable、Callable、Future以及相关的子类,总结如下:
- Runnable可以直接被Thread执行,但是没有返回值
- Callable执行之后有返回值,但是只能提交给线程池执行。
- Future定义了一系列关于任务取消的接口方法
- FutureTask是Future唯一实现类,它也实现了Runnable接口
- 线程池submit Callable和Runnable时最终都会转换成FutureTask
- FutureTask被执行时是被当成Runnable使用的,执行了内部保存的Callable的call方法