自Promise被纳入ECMAScript6标准后,各大浏览器几乎都实现了Promise标准。
以下是ecmascript6里promise的典型用法,为了解决一个线程的任务完成后,再去执行另一个任务。
var promise = new Promise(function(resolve, reject) { $.ajax({ success: function() { resolve(); }, failure: function() { reject(); } }) }); promise.then(function() { //调用了resolve或者reject之后 });
Java实现Promise相比较ECMAScript6来说,有些不同。
Javascript不管怎么样都是单线程的,即使ajax在浏览器内核层面表现出多线程,但是执行到js端还是单线程的。而在java上实现promise机制则复杂的多。具体表现在:
1)即使在上一个线程执行任务后,下一个任务可以在同一个线程中执行,但是加入任务的操作不得不是同步的
Netty4源码:io.netty.util.concurrent.DefaultPromise
@Override
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
if (isDone()) {
notifyLateListener(listener);
return this;
}
//加入时必须同步
synchronized (this) {
if (!isDone()) {
if (listeners == null) {
listeners = listener;
} else {
if (listeners instanceof DefaultFutureListeners) {
((DefaultFutureListeners) listeners).add(listener);
} else {
final GenericFutureListener<? extends Future<V>> firstListener =
(GenericFutureListener<? extends Future<V>>) listeners;
listeners = new DefaultFutureListeners(firstListener, listener);
}
}
return this;
}
}
notifyLateListener(listener);
return this;
}
2)在多个平行任务(线程)结束后,再去执行一个任务的case (Promise.all)的时候,判断任务是否结束的计数操作也不得不是同步的。
Netty源码:io.netty.channel.group.DefaultChannelGroupFuture
private final ChannelFutureListener childListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
boolean success = future.isSuccess();
boolean callSetDone;
// 判断所有的task是否已经结束也必须同步
synchronized (DefaultChannelGroupFuture.this) {
if (success) {
successCount ++;
} else {
failureCount ++;
}
callSetDone = successCount + failureCount == futures.size();
assert successCount + failureCount <= futures.size();
}
if (callSetDone) {
if (failureCount > 0) {
List<Map.Entry<Channel, Throwable>> failed =
new ArrayList<Map.Entry<Channel, Throwable>>(failureCount);
for (ChannelFuture f: futures.values()) {
if (!f.isSuccess()) {
failed.add(new DefaultEntry<Channel, Throwable>(f.channel(), f.cause()));
}
}
setFailure0(new ChannelGroupException(failed));
} else {
setSuccess0();
}
}
}
};