文章目录
博文所在专栏里有更多相关内容,如JAVA NIO、Reactor反应器模式等,欢迎阅读与交流。
文字来源于读书笔记及个人心得,可能有引用其他博文,若引用了你的文字请联系我,我会加上来源,或者删除相关内容。
四 Future异步回调模式
(一)join合并
join操作的作用,就是完成异步阻塞的工作——阻塞当前的线程,直到异步的并发线程的执行完成。
但join合并有一个很大的问题,就是没有返回值。
public static void main(String args[]) {
Thread hThread = new HotWarterThread();
Thread wThread = new WashThread();
hThread.start();
wThread.start();
try {
// 合并烧水-线程
hThread.join();
// 合并清洗-线程
wThread.join();
Thread.currentThread().setName("主线程");
Print.tcfo("泡茶喝");
} catch (InterruptedException e) {
Print.tcfo(getCurThreadName() + "发生异常被中断.");
}
Print.tcfo(getCurThreadName() + " 运行结束.");
}
join方法的三个重载版本:
(1)void join(): 等待乙方线程执行结束,甲方线程重启执行。
(2)void join(long millis): 等待乙方线程执行一段时间,最长等待时间为 millis 毫秒。超过millis 毫秒后,不论乙方是否结束,甲方线程重启执行。
(3)void join(long millis, int nanos): 等待乙方线程执行一段时间,最长等待时间为 millis 毫秒,加nanos 纳秒。超过时间后,不论乙方是否结束,甲方线程重启执行。
强调一下容易混淆的几点:
(1)join方法是实例方法,需要使用线程句柄去调用,如thread.join();
(2)执行到join代码的时候,不是thread所指向的线程阻塞,而是当前线程阻塞;
(3)thread线程代表的是被合并线程(乙方),当前线程阻塞线程(甲方)。当前线程让出CPU,进入等待状态。
(4)只有等到thread线程执行完成,或者超时,当前线程才能启动执行。
(二)FutureTask系列类
与join()相比,通过FutureTask的get方法可以获取异步结果,然而这个过程中,主线程也会被阻塞的,并不支持非阻塞异步。
了解FutureTask之前先认识下Callable和Future接口
Callable接口
Java语言在1.5 版本之后重新定义了一个新的、类似Runnable的接口——Callable接口,位于java.util.concurrent包中,将run方法改为了call方法,并且带上了返回值,允许异常抛出。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
——不能直接代替Runnable作为Thread的传入参数
Future接口
主要提供了3大功能:
(1)获取并发的任务完成后的执行结果。
(2)能够取消并发执行中的任务;
(3)判断并发任务是否执行完成;
Future接口的代码如下:
public interface Future<V> {
//取消执行
boolean cancel(boolean mayInterruptRunning);
//任务是否已取消
boolean isCancelled();
//任务是否已完成
boolean isDone();
//阻塞地获取并发任务执行的结果,直到并发任务执行完成。
V get() throws InterruptedException, ExecutionException;
//阻塞地获取并发任务执行的结果,直到并发任务执行完成或阻塞时间超过超时时间。
V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask 类
位于 java.util.concurrent包,构造函数的参数为 Callable,并且间接的继承了Runnable接口。
既能当做一个Runnable 作为 target ,直接被Thread执行;也能作为Future用来去取得Callable的计算结果。
//构造器源码
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask内部有:
一个 Callable类型的成员,从构造器传入,用来保存并发执行的 Callable类型的任务。
一个 run方法,是Runnable接口在FutureTask内部的实现,该方法会执行到callable成员的call方法。
一个 Object 类型的重要成员:outcome,用来保存callable成员call方法的执行结果,供FutureTask类的get实例方法获取。
示例:
static class HotWarterJob implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
try {
System.out.println("洗好水壶");
System.out.println("灌上凉水");
System.out.println("放在火上");
//线程睡眠一段时间,代表烧水中
Thread.sleep(SLEEP_GAP);
System.out.println("水开了");
} catch (InterruptedException e) {
System.out.println(" 发生异常被中断.");
return false;
}
System.out.println(" 运行结束.");
return true;
}
}
public static void main(String args[]) {
Thread.currentThread().setName("主线程");
Callable<Boolean> hJob = new HotWarterJob();
FutureTask<Boolean> hTask = new FutureTask<Boolean>(hJob);
Thread hThread = new Thread(hTask, "** 烧水-Thread");
hThread.start();
try {
//hThread.join();
Boolean warterOk = hTask.get();
} catch (InterruptedException e) {
System.out.println(getCurThreadName() + "发生异常被中断.");
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(getCurThreadName() + " 运行结束.");
}
(三)Guava 的异步回调
Guava 引入了异步回调机制,可以使用回调函数 对异步线程的结果进行处理 ,而 不用阻塞 地等待执行结果。
google 提供的扩展包 Guava,位于com.google.common.util.concurrent
使用流程
-
创建Java的 Callable的异步任务实例:
Callable<Boolean> hJob = new HotWarterJob();
-
创建java线程池:
-
//java 线程池 ExecutorService jPool = Executors.newFixedThreadPool(10);
-
-
通过java线程池获取Guava线程池:
-
//Guava线程池 ListeningExecutorService gPool = MoreExecutors.listeningDecorator(jPool);
-
-
提交异步任务到Guava线程池,获取ListenableFuture 实例:
ListenableFuture<Boolean> hFuture = gPool.submit(hJob);
-
创建回调的 FutureCallback 实例,通过Futures.addCallback,将回调逻辑绑定到ListenableFuture 实例
-
Futures.addCallback( hFuture , new FutureCallback<Boolean>(){ public void onSuccess(Boolean r){ //成功时候的回调逻辑 } public void onFailure(Throwable t){ //异常时候的回调逻辑 } });
-
-
完成以上步骤,当异步逻辑执行完成后,就会回调FutureCallback 实例中的回调代码。
FutureCallback: Future回调接口
FutureCallback 是一个新增的接口,用来填写回调逻辑
public interface FutureCallback<V> {
//在异步线程执行成功时回调
void onSuccess(@Nullable V var1);
//在异步线程执行异常时回调
void onFailure(Throwable var1);
}
ListenableFuture:可监听的Future接口
ListenableFuture 是 Guava 对java 的Future接口的扩展
package com.google.common.util.concurrent;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
public interface ListenableFuture<V> extends Future<V> {
//将FutureCallback回调逻辑,绑定到异步线程上
void addListener(Runnable var1, Executor var2);
}
绑定回调
addListener方法无法被直接调用,需借助Guava的Futures 工具类的addCallback 静态方法
//hFuture为ListenableFuture实例
Futures.addCallback( hFuture , new FutureCallback<Boolean>(){
public void onSuccess(Boolean r){
//成功时候的回调逻辑
}
public void onFailure(Throwable t){
//异常时候的回调逻辑
}
});
获取ListenableFuture实例
向Guava线程池提交Callable实例。
Netty的异步回调
与Guava一样有自己的异步回调体系:
- 继承Java的Future接口,得到属于自己的同名的Future接口,位于io.netty.util.concurrent ,一般不会直接使用,而是使用子接口,如 ChannelFuture 接口
- 引入新接口GenericFutureListener,监听异步任务的监听器
Netty的Future接口
一般不会直接使用,而会使用其子类,例如ChannelFuture
public interface Future<V> extends java.util.concurrent.Future<V> {
// 任务执行成功,返回true
boolean isSuccess();
// 任务被取消,返回true
boolean isCancellable();
// 异步任务执行失败,返回异常
Throwable cause();
// 添加Listener,异步非阻塞处理回调结果
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
// 移除Listener
Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
// 同步阻塞等待任务结束;执行失败话,会将“异常信息”重新抛出来
Future<V> sync() throws InterruptedException;
Future<V> syncUninterruptibly();
// 同步阻塞等待任务结束,和sync方法一样,只不过不会抛出异常信息
Future<V> await() throws InterruptedException;
Future<V> awaitUninterruptibly();
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
boolean await(long timeoutMillis) throws InterruptedException;
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
boolean awaitUninterruptibly(long timeoutMillis);
// 非阻塞,获取执行结果
V getNow();
// 取消任务
@Override
boolean cancel(boolean mayInterruptIfRunning);
}
ChannelFuture
ChannelFuture 子接口表示 Channel 通道 I/O 操作的异步任务;如果在 Channel 的异步 I/O 操作完成后,需要执行回调操作,就需要使用到 ChannelFuture 接口。
//connect是异步的,仅提交异步任务
ChannelFuture future= bootstrap.connect(new InetSocketAddress("www.manning.com",80));
//connect的异步任务真正执行完成后,future回调监听器才会执行
future.addListener(new ChannelFutureListener(){
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if(channelFuture.isSuccess()){
System.out.println("Connection established");
}else{
System.err.println("Connection attempt failed");
channelFuture.cause().printStackTrace();
}
}
});
GenericFutureListener接口
GenericFutureListener的父接口EventListener是一个空接口,没有任何的抽象方法,是一个仅仅具有标识作用的接口,一般会使用它的子接口,如ChannelFutureListener。
public interface GenericFutureListener<F extends Future<? >> extends EventListener {
//监听器的回调方法
void operationComplete(F var1) throws Exception;
}