[JAVA学习笔记-64] Future与Callable的关联

来源:互联网 发布:sql出生日期怎么算年龄 编辑:程序博客网 时间:2024/04/28 06:14
一个Future对象与一个被提交到ExecutorService对象去执行的Callable对象相关联,使用Future可以:
1、取消一个Callable任务
2、获取Callable任务的返回值
3、查看Callable任务是否被取消
4、查看Callable任务是否已经完成执行(正常执行完毕,异常退出,被取消)

Future对象与Callable对象是如何关联的?


1、提交给线程池
public <T> Future<T> submit(Callable<T> task) {
return schedule(task, 0, TimeUnit.NANOSECONDS);
}
2、schedule的处理

public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay,
                                           TimeUnit unit) {
        if (callable == null || unit == null)
            throw new NullPointerException();
        RunnableScheduledFuture<V> t = decorateTask(callable,new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)));
        delayedExecute(t);
        return t;
    }

说明:
1、public interface ScheduledFuture<V> extends Delayed, Future<V> {}
ScheduledFuture 实质是继承自 Future<V>,因此业务代码用Future<V> 类型的对象接收这个返回的对象即可
 
2、返回给业务的实质是 RunnableScheduledFuture<V> 对象
public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, ScheduledFuture<V> {
boolean isPeriodic();
}

RunnableScheduledFuture<V> --> ScheduledFuture<V> --> Future<V>
从命名可以看到,RunnableScheduledFuture 具备Future的功能,同时它应该也具备Runnable功能,也就是说,它其实是一个被执行的task对象,
并且这个对象还具备Scheduled功能,即,它是可以被调度的(单次/周期执行)

3、从 delayedExecute(t) 可以看到实际运行的是经过封装的Callable对象,即t,而不是直接运行Callable对象
/**
* Main execution method for delayed or periodic tasks.  If pool
* is shut down, rejects the task. Otherwise adds task to queue
* and starts a thread, if necessary, to run it.  (We cannot
* prestart the thread to run the task because the task (probably)
* shouldn't be run yet,) If the pool is shut down while the task
* is being added, cancel and remove it if required by state and
* run-after-shutdown parameters.
*
* @param task the task
*/
private void delayedExecute(RunnableScheduledFuture<?> task)

4、关于 decorateTask
protected <V> RunnableScheduledFuture<V> decorateTask(
Callable<V> callable, RunnableScheduledFuture<V> task) {
return task;
}

可见这个方法其实啥也没干,就是返回了 new ScheduledFutureTask<V>(callable,triggerTime(delay, unit)

5、那么我们来看看 ScheduledFutureTask<V> 的构造
public interface RunnableFuture<V> extends Runnable, Future<V>
public class FutureTask<V> implements RunnableFuture<V>

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;       // ensure visibility of callable
}

FutureTask的构造函数,保存了一个Callable对象。从FutureTask的继承关系,可以看到它将分配一个线程来执行,并且,它具备Future<V>的功能。

private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V>

从继承关系可以看到,ScheduledFutureTask 实际上是一个 FutureTask ,这样,业务创建的Callable对象,通过FutureTask保存在一个
ScheduledFutureTask 对象中。

综述:
简单地讲,用户提交的Callable对象,最终经过FutureTask的封装,经由线程池分配线程并执行。换句话说,业务代码调用submit返回的Future<V>
对象,正是被线程池执行的那个对象,而由此调用get返回的正是被封装的那个Callable对象本身的result。
由此似乎也可以看到FutureTask存在的意义,即,它同时具备Future以及Task的功能,用它来封装一个Callable对象,只要运行这个Decorated Callable
object,并调用实现的Future<V>接口的get方法,就可以获取这个Callable对象的执行结果了。换句话说,业务代码一般并不需要直接使用FutureTask,使用
Future就可以了。


3、执行结果的返回
task被加入到调度队列,剩下的事情就不在此赘述了。这里我们关注的是Future接口,因此再来看下,get方法是如何实现的。
这是FutureTask的run方法,任务队列的执行线程,应该是通过调用该方法,来调用被封装的Callable对象的call()方法,进而执行的。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> 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);   //设置Callable对象的执行结果

} 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);
}
}

结果的返回:

FutureTask实现了Future接口的get方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}

report方法的实现:
private V report(int s) throws ExecutionException {
Object x = outcome;  
//这里的outcome,就是run方法中,set(result) 执行后保存result的对象。它被定义成Object,意味着Callable<V>的V必须是
//一个Object对象,而不能是JAVA本身的primitive types,这就是可以用Integer而不能用int的原因,因为get返回的是一个对象。
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
0 0
原创粉丝点击