一 、使用方式一般为两种:
1 . 创建线程执行FutureTask并通过FutureTask得到异步结果;
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
// 1.创建任务,这个FutureTask可以是Callable的也可以是Runnable的
FutureTask task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(3000); // 模拟异步任务执行
return "我是异步结果";
}
});
// 2.创建线程执行任务
Thread thread = new Thread(task);
thread.start();
// 3.主线程模拟在异步任务之间执行其他任务
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 4.得到异步任务结果
System.out.println(task.get());
System.out.println("运行时间 : " + (System.currentTimeMillis() - start) + "ms");
}
运行结果:
我是异步结果
运行时间 : 6002msProcess finished with exit code 0
2 . 使用线程池管理线程通过Future接口得到结果;
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future future = executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
Thread.sleep(3000);
return "我是异步结果";
}
});
System.out.println("主线程工作");
Thread.sleep(6000);
System.out.println(future.get());
System.out.println("运行时间 : " + (System.currentTimeMillis() - start) + "ms");
}
运行结果:
主线程工作
我是异步结果
运行时间 : 6012ms
注意:
异步结果用Future或者FutureTask都可以获取。Future是接口,FutureTask是实现了这个接口的实现类。FutureTask除了实现Future接口外,还实现了Runnable接口。所以不论是Callable任务还是Runnable任务都可以用FutureTask完成,而这两种都会最终都会调用Callable的call方法,对于Runnable实现调用call的方式是使用了适配器模式。
(图片来源网上,侵删)
debug第二个程序,我们可以看到这时定义的Future也是FutureTask实现的。
二、异步原理
1. 异步模型:
可以总结为生产者和消费者,生产者就是线程处理任务,消费者就是调用者得到任务结果。那生产者消费者模型还有缓冲区啊,这里面就是用FutureTask中的outcome字段在任务结束后缓存结果。
2 . 异步流程:
主线程main开启线程a执行任务,并马上返回Future,如果是刚新建任务,状态置为new。当main线程想取线程a执行的结果(调用了FutureTask的get方法)时,就查看这个任务的state是否执行完成(completing),如果还没有完成就阻塞当前线程(main)等待线程a执行完成。