异步执行,听起来很抽象,简单的来说,主线程不管这件事了,起了个新的线程去做这件事,那么这件事的执行过程,对于主线程而言,就是异步的。
可以把这件事的执行过程给抽象出来,定义为一个方法,方法的内容就是这件事的具体执行过程。
比如Runnable。
Runnable
Runnable接口只有一个run()方法,run()里面的内容就是这件事执行的过程,怎么去异步呢?
new Thread(new Runnable(){
@Override
public void run() {
// 这件事的逻辑代码
}
}.start();
如果主线程去执行上面这段代码,那么这件事的执行对于主线程来说,就是异步执行的。
举个例子,小明吃完了饭,是不是得去洗碗呐,如果小明是单身狗,那洗碗这件事没人帮的了小明,所以小明就得花这10几分钟来洗碗,但小明有钱啊,买个洗碗机,洗碗这件事就不用小明这个主线程来做了,当然是用洗碗机这个新线程去洗碗,洗碗这件事对小明来说就是异步的了。
买的第一代的洗碗机,还有有点low,这洗碗机最后会把碗吃掉,对应于run()不会返回洗完的碗,小明还得看着洗碗机洗碗,不能做其他事情,等于主线程调用了洗碗线程.join()。
如果想洗碗机洗完碗后给碗,可用Callable。但暂时还做不到你不用看着洗碗机洗碗。
Callable
Runnable的run()方法是void,Callable的call()方法,返回的是泛型V,在用法上有着很大的区别,Runnable的实现类实例,可以作为Thread对象的参数,也就是Runnable可以与Thread直接关联,Callable要被Thread执行的话,只能转成Runnable。
是你的话,怎么解决Callable与Thread的关联问题?
那我就写个类,实现Runnable接口,把Callable注入进来,然后run()方法就执行Callable对象的call()方法。
哈哈,其实java提供了Callable与Thread的桥梁,FutureTask就是这么一回事,但是呢FutureTask这名字总感觉是Callable和Runnable关系不大…
Callable<Bowl> washBowl = new Callable<Bowl>(){
@Override
public Bowl call() {
// 洗碗这件事,可能会花10分钟,或者15分钟,这个时间对小明来说透明的
}
}
// 把washBowl这件事转为Thread可执行的事情,还得需要FutureTask这个中间者
FutureTask<Bowl> washBowlMiddler = new FutureTask<>(washBowl);
// 洗碗机去异步地洗碗
Thread washBowlMachine = new Thread(washBowlMiddler).start();
// 怎么拿到洗干净后的碗呢,有个接口叫Future
Bowl cleanBowls = washBowlMiddler.get();
从上面的例子可以看到,对于Callable的结果V,需要借助FutureTask的get()来获取,Callable也蛮可怜的,什么东西都要这个靠中间者来帮忙…
FutureTask的get()也是一个阻塞的方法,这个方法是FutureTask继承于Future接口的get()方法,所以FutureTask = 实现了Callable + 实现了Runnbale + 实现Future 。到这好像能知道FutureTask这个名字的由来了,首先他包含了要做的事情(Task),还可以拿到异步执行事情的结果(Future)。
所以最后一行代码,主线程会阻塞在那,等于小明还是只能看着洗碗机洗10分钟或者更久,但好处是小明可以拿到洗干净的碗。
那我又想要洗干净的碗,又想不盯着洗碗机,可以躺床上打游戏,那可以用Guava。
Guava
第三代洗碗机终于发布,小明直呼好家伙,直接入手,这下可以不用盯着洗碗机洗完了,还能给出干净的碗。
想想看,为什么小明之前要傻傻的阻塞在那盯着洗碗机把碗洗完?因为小明不知道洗碗机会洗多久,第三代改良后,洗碗机在洗完后,会哔哔哔的叫,这时候小明就知道碗原来洗好了。
在Guava中,有几个组件需要提前去了解下,
- FutureCallback,这个接口名字里包含callback,大概知道是回调的东西,还带Future,那应该也能知道是异步执行的东西,所以这组件就是异步执行结果的回调,要注意的是,这个结果可能是执行成功的结果,也可能是执行出错的结果,所以FutureCallback有两个方法:
void onSuccess(@Nullable V var1); // 成功的结果,那就传入成功的结果
void onFailure(Throwable var1); // 失败的结果,那就传失败的原因
说白了,就是做善后处理的接口。
- ListenableFuture,名字也带有Future,真的和Future过不去了…,姑且就把这个当做是 FutrueTask的加强版,FutureTask执行完后的结果想要执行到上面的FutureCallback善后工作,就得和它关联起来,所以ListenableFuture就由一个addListener(Runnable r, Executor e)方法来进行关联,很明显,这个方法是观察者模式,至于ListenableFuture怎么合FutureCallback如何关联起来,Guava也提供了专门的步骤:
Futures.addCallback(listenableFuture, new FutureCallBack<Bowl>() {
@override
public void onSuccess(Bowl cleanBowls) {
// 洗碗机洗完了,传进来干净的碗
}
@Override
public void onFailure(Throwable t) {
// 洗碗机故障了,传进来故障的对象
}
}
可类比下支付的回调。
现在看起来还比较抽象,没事,我也觉得抽象,所以明天再写。