并发编程 —— 自己写一个异步回调 API
1. 前言
在并发编程中,异步回调的效率不言而喻,在业务开发中,如果由阻塞的任务需要执行,必然要使用异步线程。并且,如果我们想在异步执行之后,根据他的结果执行一些动作。
JDK 8 之前的 Future 只能解决上面需求的一半问题,即异步执行,返回一个 Future,需要程序员调用 get 方法等待,或者使用 isDone 轮询。
效率不高。
JDK 8 新出的 CompletableFuture API 可以解决这个问题。但他的 API, 说实话,不太好用。
我们只想要一个简单的 API,能实现我们的回调功能。
我需要 3 个功能:
- 能通过 get 之类的方法返回结果。
- 能设置监听器进行回调。
- 可以在业务线程中设置成功或者失败。
楼主写一个简单的例子,借鉴了 Netty 的异步 API,希望能起到抛砖引玉的作用。
2. 设计
根据我们的需求:
第一,我们需要一个类,拥有 get 方法和 addListener 方法。
第二,我们需要一个类,能够回调我们设置的监听器。
第三,我们需要一个类,能够在业务线程中设置成功或者失败。
3. 初步实现
设计一个监听器接口:
/**
* 监听器
* @author stateis0
*/
public interface MyListener {
/**
* 子类需要重写此方法,在异步任务完成之后会回调此方法。
* @param promise 异步结果占位符。
*/
void operationComplete(MyPromise promise);
}
设计一个异步占位符,类似 Future:
/**
* 异步执行结果占位符
*
* @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();
}
4. 写个 Demo 测试一下
/**
* @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 设计,易懂好用。
并发编程 —— 自己写一个异步回调 API的更多相关文章
- Python并发编程06 /阻塞、异步调用/同步调用、异步回调函数、线程queue、事件event、协程
Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件event.协程 目录 Python并发编程06 /阻塞.异步调用/同步调用.异步回调函数.线程queue.事件 ...
- 2018.7.20 编程题: 写一个Singleton出来。
编程题: 写一个Singleton出来. Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 一般Singleton模式通常有几种种形式: 第一种形式: 定义 ...
- Java并发编程(06):Lock机制下API用法详解
本文源码:GitHub·点这里 || GitEE·点这里 一.Lock体系结构 1.基础接口简介 Lock加锁相关结构中涉及两个使用广泛的基础API:ReentrantLock类和Condition接 ...
- [Boost基础]并发编程——asio网络库——异步socket处理
异步服务器端 #include <conio.h> #include <iostream> using namespace std; #include <boost/as ...
- 大一C语言学习笔记(11)---编程篇--写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积,要求 0 bug;
考核内容: 写一个程序,可以获取从键盘上输入的的三个数,并能够判断是否可以以这三个数字作为边长来构成一个三角形,如果可以的话,输出此三角形的周长及面积: 答案: #include<stdio.h ...
- python 并发编程 同步调用和异步调用 回调函数
提交任务的两张方式: 1.同步调用 2.异步调用 同步调用:提交完任务后,就在原地等待任务执行完后,拿到结果,再执行下一行代码 同步调用,导致程序串行执行 from concurrent.future ...
- 手写一个json格式化 api
最近写的一个东西需要对json字符串进行格式化然后显示在网页上面. 我就想去网上找找有没有这样的api可以直接调用.百度 json api ,搜索结果都是那种只能在网页上进行校验的工具,没有api. ...
- python语法基础-并发编程-进程-进程池以及回调函数
############### 进程池 ############## """ 进程池的概念 为什么会有进程池? 1,因为每次开启一个进程,都需要创建一个内存空间 ...
- Python并发编程-进程池及异步方式
进程池的基本概念 为什么有进程池的概念 效率问题 每次开启进程,都需要开启属于这个进程的内存空间 寄存器,堆栈 进程过多,操作系统的调度 进程池 python中的 先创建一个属于进程的池子 这个池子指 ...
随机推荐
- AEAI WM v1.6.0 升级说明,开源工作管理系统
1 升级说明 AEAI WM v1.6.0版是AEAI WM v1.5.0版工作管理系统的升级版本,本次升级的系统是基于AEAI DP 3.8.0_20170228进行打包部署的,对产品中的功能及BU ...
- Codeforces Round #425 (Div. 2) B. Petya and Exam(字符串模拟 水)
题目链接:http://codeforces.com/contest/832/problem/B B. Petya and Exam time limit per test 2 seconds mem ...
- 在redis中使用lua脚本
在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 不过lua也会有很多限制,在使用的时候要注意. 在Redis中执行Lu ...
- Swift5 语言参考(九) 泛型和参数
本章介绍泛型类型,函数和初始值设定项的参数和参数.声明泛型类型,函数,下标或初始化程序时,可以指定泛型类型,函数或初始化程序可以使用的类型参数.当创建泛型类型的实例或调用泛型函数或初始化程序时,这些类 ...
- 移动一根火柴使等式成立js版本(递归)
修改成递归版本 思路: 1.设定规则数组,比如:1加一根火柴只可以变成7. 2.设定方法数组,比如:一个数增加了一根火柴,其他的数必然减少一根火柴. 3.增加Array方法,由元素名和方法,得到规则对 ...
- python(29)----时间模块
time模块 1. 三种时间表现形式 时间戳(timestamp) 格式化的时间字符串 元祖/结构化时间(struct_time) 2. 时间戳(timestamp) 通常来说,时间戳表示的是从197 ...
- POJ 2707
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; int ...
- jq04--jq与ajax
之前我们学习了一些有关jq函数的知识,现在我们看看jq与ajax方面的一些东西: 1.ajax(Asynchronous JavaScript and XML 异步的JavaScript与xml): ...
- java8之lambda表达式(2)-方法引用
方法引用使用的地方也是在函数式接口,使用方法引用可以使代码更加简单和便捷 在如下代码中 根据List中字符串长度排序的代码可以写成如下: public static void test1_() { L ...
- Android_view的生命周期
onFinishInflate() 当View中所有的子控件均被映射成xml后触发 onMeasure( int , int ) 确定所有子元素的大小 onLayout( boolean , int ...