在并发编程中,异步回调的效率不言而喻,在业务开发中,若是由阻塞的任务须要执行,必然要使用异步线程。而且,若是咱们想在异步执行以后,根据他的结果执行一些动做。java
JDK 8 以前的 Future 只能解决上面需求的一半问题,即异步执行,返回一个 Future,须要程序员调用 get 方法等待,或者使用 isDone 轮询。程序员
效率不高。编程
JDK 8 新出的 CompletableFuture API 能够解决这个问题。但他的 API, 说实话,不太好用。promise
咱们只想要一个简单的 API,能实现咱们的回调功能。并发
我须要 3 个功能:异步
楼主写一个简单的例子,借鉴了 Netty 的异步 API,但愿能起到抛砖引玉的做用。ide
根据咱们的需求: 第一,咱们须要一个类,拥有 get 方法和 addListener 方法。 第二,咱们须要一个类,可以回调咱们设置的监听器。 第三,咱们须要一个类,可以在业务线程中设置成功或者失败。测试
设计一个监听器接口:this
/** * 监听器 * @author stateis0 */
public interface MyListener {
/** * 子类须要重写此方法,在异步任务完成以后会回调此方法。 * @param promise 异步结果占位符。 */
void operationComplete(MyPromise promise);
}
复制代码
设计一个异步占位符,相似 Future:spa
/** * 异步执行结果占位符 * * @author stateis0 */
public class MyPromise {
/** 监听器集合*/
List<MyListener> listeners = new ArrayList<MyListener>();
/** 是否成功*/
boolean success;
/** 执行结果**/
Object result;
/** 设置事变计数器**/
int failCount;
/** * 设置成功,并通知全部监听器。 * @param result 结果 * @return 是否成功 */
public boolean setSuccess(Object result) {
if (success) {
return false;
}
success = true;
this.result = result;
signalListeners();
return true;
}
/** * 通知全部监听器,回调监听器方法。 */
private void signalListeners() {
for (MyListener l : listeners) {
l.operationComplete(this);
}
}
/** * 设置失败 * @param e 异常对象 * @return 设置是否成功 */
public boolean setFail(Exception e) {
if (failCount > 0) {
return false;
}
++failCount;
result = e;
signalListeners();
return true;
}
/** * 是否成功执行 */
public boolean isSuccess() {
return success;
}
/** * 添加监听器 * @param myListener 监听器 */
public void addListener(MyListener myListener) {
listeners.add(myListener);
}
/** * 删除监听器 * @param myListener 监听器 */
public void removeListener(MyListener myListener) {
listeners.remove(myListener);
}
/** * 获取执行结果 */
public Object get() {
return result;
}
}
复制代码
咱们但愿使用线程池执行此类任务,因此须要一个自定义的 Runnable,而在这个 Runnable 中,咱们须要作一些简单的手脚:
/** * 一个任务类,经过重写 doWork 方法执行任务 * @param <V> 返回值类型 * @author stateis0 */
public abstract class MyRunnable<V> implements Runnable {
final MyPromise myPromise;
protected MyRunnable(MyPromise myPromise) {
this.myPromise = myPromise;
}
@Override
public void run() {
try {
V v = doWork();
myPromise.setSuccess(v);
} catch (Exception e) {
myPromise.setFail(e);
}
}
/** * 子类须要重写此方法。并返回值,这个值由 Promise 的 get 方法返回。 */
public abstract V doWork();
}
复制代码
/** * @author stateis0 */
public class MyDemo {
public static void main(String[] args) {
// 占位对象
final MyPromise myPromise = new MyPromise();
final Dto dto = new Dto();
// 线程池
Executor executor = Executors.newFixedThreadPool(1);
// 异步执行任务,
executor.execute(new MyRunnable<String>(myPromise) {
@Override
public String doWork() {
return dto.doSomething();
}
});
// 添加一个监听器
myPromise.addListener(new MyListener() {
// 当任务完成后,就执行此方法。
@Override
public void operationComplete(MyPromise promise) {
// 获取结果
String result;
// 若是任务成功执行了
if (promise.isSuccess()) {
// 获取结果并打印
result = (String) promise.get();
System.out.println("operationComplete ----> " + result);
}
// 若是失败了, 打印异常堆栈
else {
((Exception) promise.get()).printStackTrace();
}
}
});
}
}
class Dto {
public String doSomething() {
System.out.println("doSomething");
// throw new RuntimeException("cxs");
return "result is success";
}
}
复制代码
执行结果:
doSomething
operationComplete ----> result is success
复制代码
符合咱们的预期。咱们但愿在业务对象 Dto 的 doSomething 成功返回以后,回调监听器的 operationComplete 方法。若是失败,打印异常堆栈。
固然,总体代码比较简单,仅仅只是抛砖引玉。
实际上,若是直接向 Callable 或者 Runnable 传入一个业务对象,当 call 方法或者 run 方法执行完毕,就能够根据执行结果执行咱们的业务对象的方法了。这样就是一个最简单直接的异步回调。
只是这样过于耦合。
异步任务和业务的任务耦合在了一块儿,而且不能添加多个监听器,也没法使用 promise 的 setSuccess 功能和 setFail 功能,这两个功能能够在业务线程中设置成功或者失败,灵活性更高。
关于异步,咱们能够多看看 Netty 的 API 设计,易懂好用。