更多并发相关内容,查看==>Java 线程&并发学习目录
目录
Java 异步编程之 FutureTask
1、自定义Demo 获取线程执行结果
2、Callable 接口
3、Future 接口
4、FutureTask 类
4.1、FutureTask 部分源码说明
1、自定义Demo 获取线程执行结果
在Java多线程编程中,创建线程执行一般有两种方法
- 实现Runnable接口,创建Task然后提交给线程执行
- 继承Thread类,创建新线程执行
但是这两种方法都有个同样的问题,无法获得线程执行的结果,run方法都是返回void
其实也可以有方法去解决,再包装一层run方法,使得获取到执行结果,再通过另一个方法去获取该结果,例如如下的样例代码
最后输出的内容
从结果上可以看出来,确实是利用了创建的新线程计算出了当前的时间戳,符合我们最初级的要求
原理也非常的简单,只是重新包装了一下Runnable接口,在其void run()
方法中计算出期望的结果,然后存储到data字段,最后在主线程的方法中调用resutl.getData方法获取线程的结果,虽然这个实现是非常低级的,但是也已经说明了一些问题,我们想要实现从线程中获取计算结果,必须侵入到void run() 方法中,然后进行改造存储计算结果,提供其他的方法供使用方取得计算结果,在Java5中添加的Callable 接口就是为了实现该功能,接下来
2、Callable 接口
Callable 接口只有一个方法,V call() 提供给外界使用,其实和上文中自定义的Result接口功能接近,正如该接口的文档描述所说,获取任务执行的结果,运行中可能会抛出异常,和Runnable接口功能不同
一般不会单独使用这个Callable接口,更多的是在异步的获取线程结果,或者回调结果等
3、Future 接口
public interface Future {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Java5中新增的接口,主要用途同样是在线程操作中,例如某线程得长时间等待才能得出计算结果,但是又不必须立马就使用,就可以使用该接口,把线程的计算结果赋值给Future对象中,然后等到使用的时候通过get方法就可以轻松的获取计算结果,继续操作,避免长时间的等待,可以提高效率
4、FutureTask 类
上面的demo肯定有关注到主方法中存在一个睡眠1s的操作,其实是为了确保下面的resutl.getData方法一定可以获取到值。这主要是没有Future类的get方法的功能,当没计算出结果时,就会长时间等待轮询,直到结果出现,
这点也恰好说明了Future是异步处理,同步等待结果,切记勿随便在主线程这样操作,避免夯住主线程,造成服务不可用的情况。
FutureTask就是吸收了Future的特点,再组合Callable的能力,从而实现获取线程结果,并且可以利用线程中断随时终止线程的执行,如下FutureTask类图
public class FutureTask implements RunnableFuture {
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
private Callable callable;
private Object outcome;
private volatile Thread runner;
private volatile WaitNode waiters;
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
....
如上FutureTask部分代码,就是传入一个callback对象,或者已经包含结果的Runnable对象,因为FutureTask本身也是继承了Runnable接口,所以可以直接当做一个Task丢给线程、线程池去运行的,看看FutureTask具体的样例代码
FutureTask是支持传递callback对象以及runnable对象的,不过由于rannable是不支持返回结果,必须传入特定的结果然后返回。所以更多的都是传入callback接口实现的对象
最后使用new Thread(futureTask).start();
就可以了
4.1、FutureTask 部分源码说明
- 1、为什么传递Runnable对象也可以
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
// 创建了一个新的callable的对象,重新包装一层而已,其实是个RunnableAdapter对象而已
this.state = NEW; // ensure visibility of callable
}
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();
// 传递的是runnable对象,真正去调用runnable的run方法
return result;
// 返回已经设置好的结果
}
}
- 2、FutureTask 的run方法
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();
// 就是在这里调用callable.call 方法
// 获取计算结果
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// ran为true,则意味着存在有效的结果,把result存储到outcome对象中
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);
}
}
到这里对Java的FutureTask类就有了比较清晰的认识,再次强调一点的是,get方法会长时间轮询,尽量避免在主线程执行该类操作