java I/O的经历了BIO(Blocking IO),NIO(Non-blocking IO),AIO(Async-I/O),这些IO技术的发展都是依托已java版本的更新。
本文主要研究一下java的异步回调机制,
主要针对juc包下的Future,Callable,FutureTask,更多关于java I/O历程。
**业务场景 **
假定我们有这样一个业务场景,烧一壶开水,烧一壶开水有如下几个步骤:
- 加水
- 打开煤气
- 烧水ing—(比较耗时的动作)
- 观察,烧水完成
- 最后水开之后,关闭煤气
AbstractBoilWater
public abstract class AbstractBoilWater {
protected volatile boolean isReadyFlag = false;
protected void a_addWater() {
System.out.println("1.加水");
}
protected void b_on() {
System.out.println("2.打开煤气");
}
protected void c_boiling() throws Exception {
System.out.println("3-1.烧水中.....");
Thread.sleep(5000); //模拟烧水的过程,比较耗时
System.out.println("3-2.水开了");
isReadyFlag = true;
}
protected void d_off() {
System.out.println("4.关闭煤气");
}
//烧水方法
public abstract void make() throws Exception;
protected void playGame() throws Exception {
Thread.sleep(1200);
if (!isReadyFlag) {
System.out.println("水还没烧开,玩一把游戏");
}
}
}
编写一个抽象类AbstractBoilWater,
其中c_boiling()模拟一个烧水的过程,比较耗时;
提供一个抽象方法make(),各个子类通过重写该方法,用自己的方式来花式烧水。
方法一:同步等待
方法一的实现步骤如下:
- a.加入适量的水
- b.打开煤气
- c.暗中观察,等待水烧开 —同步阻塞等待
- d.关闭煤气
代码一
public class BioBoilWater extends AbstractBoilWater{
@Override
public void make() throws Exception {
a_addWater();
b_on();
c_boiling();//同步阻塞等待
d_off();
}
}
,由代码可见在烧水的过程中,一直在同步阻塞等待,烧水的这一段时间就白白浪费了。
方法二:Future,Callable-异步执行,同步回调
- a.加入适量的水
- b.打开煤气 — 老鸟有一定的经验,目测20分钟之后可以烧开,
- c.playGame// 打一局小游戏
- d.观察水是否烧开烧开
- e.关闭煤气
方法二代码
public class NioBoilWater extends AbstractBoilWater{
@Override
public void make() throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
a_addWater();
b_on();
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
c_boiling();
return 1;
}
});
executor.shutdown();
while(!future.isDone()){
playGame();//烧水过程中,玩一局游戏
}
int a = future.get(); //同步阻塞等待
d_off();
}
}
启动一个新的线程去执行c_boiling(),执行过程中,无需同步的阻塞等待,可以去做一些其他一些有意思的事,打游戏playGame(); 最终使用future.get(),这是一个同步阻塞的操作,等待Callable事件完成。
这里注意的一点:Future只能用线程池去调用执行。
Future源码解析
public interface Future<V> {
//终止当前任务
boolean cancel(boolean mayInterruptIfRunning);
//Returns {@code true} if this task was cancelled before it completed
boolean isCancelled();
//正常完成,异常,cancel;
boolean isDone();
/**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
//阻塞--最大等待时间
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
方法三:FutureTask-异步执行,异步回调
- a.加入适量的水
- b.打开煤气
- c.playGame // 打一局小游戏
- d.主动提醒,水已烧开 ---- //主动通知-回调
- e.关闭煤气
方法三代码
public class AioBoilWater extends AbstractBoilWater {
@Override
public void make() throws Exception {
a_addWater();
b_on();
FutureTask<Integer> futureTask = new FutureTask(() -> {
c_boiling();
return 1;
}){
//当futureTask执行完之后,调用done()
@Override
protected void done() {
d_off();
}
};
new Thread(futureTask).start();
while(!futureTask.isDone()){
playGame();
}
// futureTask.get();//阻塞,
Thread.sleep(5000);
}
}
FutureTask源码分析
,FutureTask实现了Future和Runnable接口, 它可以不适用线程池,直接通过线程启动。
它最大的特点是,它有一个done()回调函数,当前任务状态为isDone时,触发。
protected void done() {}
方法四:CompletableFuture-异步执行,异步回调
使用jdk8新特性:CompletableFuture
public class CompletableFutureBiolWater extends AbstractBoilWater {
@Override
public void make() throws Exception {
a_addWater();
b_on();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
c_boiling();
} catch (Exception e) {
e.printStackTrace();
}
return 1;
});
//注册事件“监听”
future.whenComplete((v, e) -> {
/* System.out.println(v);
System.out.println(e);*/
d_off();
});
while (!future.isDone()) {
playGame();
}
}
}