在併發編程中,異步回調的效率不言而喻,在業務開發中,若是由阻塞的任務須要執行,必然要使用異步線程。而且,若是咱們想在異步執行以後,根據他的結果執行一些動做。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 設計,易懂好用。