Java --- Future
来源:互联网 发布:jdk 8u91 windows x64 编辑:程序博客网 时间:2024/06/05 18:32
Java --- Future
JDK - Futrue
JDK定义:Future代表着一个异步计算的结果,提供了检查异步计算是否完成,等待异步计算完成,获取计算结果等方法。换句话说:提交了一个计算后,需要一个接口来获取计算结果或确认计算是否完成,这个接口就是Future。
从定义上来看,Future是和一个计算绑定在一起的,因此很自然的引申出以下几个接口及实现类:
RunnableFuture<V> :代表一个可运行的Future.
RunnableScheduledFuture<V>:代表一个可以延迟运行的Future
FutureTask<V>:RunnableFuture的实现类。
FutureTask使用示例代码:
FutureTask<Long> future =new FutureTask<Long>(new Callable<Long>(){
@Override
public Long call()throws Exception {
Thread.sleep(10000);
return 1000L;
}
});
Executors.newSingleThreadExecutor().execute(future);
try {
System.out.println(future.get());
} catch (InterruptedExceptione) {
//TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionExceptione) {
//TODO Auto-generated catch block
e.printStackTrace();
}
这里创建了一个FutureTask提交给一个Executor去执行后就调用Future的get方法获取结果,get会阻塞直到FutureTask的运行任务执行完成后返回结果。
实际上java.util.concurrent.ExecutorService接口定义的向其提交任务的方法,如submit,invoke等都会返回一个Future对象供调用者来获取异步操作的执行结果,所以使用中其实并不需要开发人员自己实例化Future对象的。
<T> Future<T> submit(Callable<T> task);
……
下面看下java.util.concurrent.AbstractExecutorService的代码:
public <T> Future<T> submit(Runnabletask, T result) {
if (task ==null)thrownew NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task,result);
execute(ftask);
returnftask;
}
protected <T>RunnableFuture<T> newTaskFor(Callable<T>callable) {
returnnew FutureTask<T>(callable);
}
可以看到AbstractExecutorService会实例并返回一个FutureTask对象供调用者使用。
看下FutureTask的代码
publicvoidrun() {
if (state !=NEW ||
!UNSAFE.compareAndSwapObject(this,runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c !=null &&state == NEW) {
V result;
booleanran;
try {
result =c.call();
ran =true;
} catch (Throwableex) {
result =null;
ran =false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner =null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
ints =state;
if (s >=INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protectedvoid set(Vv) {
if (UNSAFE.compareAndSwapInt(this,stateOffset,NEW,COMPLETING)) {
outcome =v;
UNSAFE.putOrderedInt(this,stateOffset,NORMAL);// final state
finishCompletion();
}
}
public Vget() throws InterruptedException, ExecutionException {
ints =state;
if (s <=COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
就可以清楚的看到FutureTask会维持任务的执行状态,当异步计算执行完后,执行线程会调用set方法来设置状态和结果。
Netty的Future和Promise
Netty 中提供了很多有用的工具,本身也提倡说可以将Netty作为一个基础类库来使用而不是仅仅局限于网络应用。这其中也包括了Future。用Netty官方文档的话来说,就是异步计算更应该是当计算结果是触发某个操作,而不是去阻塞(当然也可以不阻塞,但总是需要调用者去主动查询)来查询计算的结果。这就是Netty Future接口的目的。
从方法列表可以看出:除了在获取结果和等待方面添加了一些有用方法外,最主要的是引入了FutureListener的概念,包括增加和删除FutureListener的方法。这些Listener也就是在异步计算的特定生命周期时刻触发的操作。
Netty Promise继承自自己的Future,是在Future可以出发监听者操作之外再增加了可以用来控制异步计算的接口。
看到增加了setSuccess,trySucess,setFailure,tryFailuer几个可以干预异步计算的方法。
看下Netty Future和Promise在Netty中的应用。下面是ServerBootStrapAcceptor类的channelRead方法,该类负责客户端和服务器建立起来的连接注册到一个EventLoopGroup。这里的注册是一个异步操作,具体注册在childGroup中的某个EventLoop来执行。childGroup.register(child) 返回的是一个ChannelFuture对象,这里给改ChannelFuture注册了一个监听器,该监听器的的功能是当注册失败时进行关闭等清除工作。
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
publicvoidoperationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child,future.cause());
}
}
});
} catch (Throwablet) {
forceClose(child,t);
}
EventLoopGroup接口的register方法定义:
ChannelFuture register(Channel channel, ChannelPromise promise);
Register方法调用的是SingleThreadEventLoop类的register方法:
public ChannelFuture register(Channelchannel) {
returnregister(channel,new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFutureregister(final Channelchannel, final ChannelPromisepromise) {
if (channel ==null) {
thrownew NullPointerException("channel");
}
if (promise ==null) {
thrownew NullPointerException("promise");
}
channel.unsafe().register(this,promise);
returnpromise;
}
可以看到register返回的是一个Promise。
探究channel.unsafe().register(this,promise),具体的实现在AbstractUnsafe.register0(promise):
privatevoid register0(ChannelPromisepromise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!ensureOpen(promise)) {
return;
}
doRegister();
registered =true;
promise.setSuccess();
pipeline.fireChannelRegistered();
if (isActive()) {
pipeline.fireChannelActive();
}
} catch (Throwablet) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
if (!promise.tryFailure(t)) {
logger.warn(
"Tried to fail the registration promise, but it is complete already. " +
"Swallowing the cause of the registration failure:",t);
}
}
}
可以看出如果注册成功则会调用promise的setsuccess方法,当出现异常时调用tryFailure方法。
DefaultPromise类
public Promise<V>setSuccess(V result) {
if (setSuccess0(result)) {
notifyListeners();
returnthis;
}
thrownew IllegalStateException("complete already: " + this);
}
privatevoidnotifyListeners() {
// This method doesn't need synchronization because:
// 1) This method is always called after synchronized (this) block.
// Hence any listener list modification happens-before this method.
// 2) This method is called only when 'done' is true. Once 'done'
// becomes true, the listener list is never modified - see add/removeListener()
Object listeners = this.listeners;
if (listeners ==null) {
return;
}
this.listeners =null;
EventExecutor executor = executor();
if (executor.inEventLoop()) {
final IntegerstackDepth = LISTENER_STACK_DEPTH.get();
if (stackDepth <MAX_LISTENER_STACK_DEPTH) {
LISTENER_STACK_DEPTH.set(stackDepth + 1);
try {
if (listenersinstanceof DefaultFutureListeners) {
notifyListeners0(this, (DefaultFutureListeners)listeners);
} else {
@SuppressWarnings("unchecked")
final GenericFutureListener<?extends Future<V>>l =
(GenericFutureListener<? extends Future<V>>)listeners;
notifyListener0(this,l);
}
} finally {
LISTENER_STACK_DEPTH.set(stackDepth);
}
return;
}
}
try {
if (listenersinstanceof DefaultFutureListeners) {
final DefaultFutureListenersdfl = (DefaultFutureListeners)listeners;
executor.execute(new Runnable() {
@Override
publicvoid run() {
notifyListeners0(DefaultPromise.this,dfl);
}
});
} else {
@SuppressWarnings("unchecked")
final GenericFutureListener<?extends Future<V>>l =
(GenericFutureListener<?extends Future<V>>)listeners;
executor.execute(new Runnable() {
@Override
publicvoid run() {
notifyListener0(DefaultPromise.this,l);
}
});
}
} catch (Throwablet) {
logger.error("Failed to notify listener(s). Event loop shut down?",t);
}
}
从DefaultPromise的代码可以看出,调用Promise的设置结果方法时会通知Listener,这里看到所有的Listener是使用一个Executor的(初始化Promise时指定的),如果Executor就是当前线程,那是会串行的执行所有的Listener,这也是Google Guava的ListnerFuture和Netty的主要区别。
NotifyListener方法的注释中有提到对Listener的修改不需要同步,因为1.一般都是在同步的代码块中,2.调用该方法的时候Future的状态是Done,这个时候是不允许修改监听者了。
我们再来看下对应于JDK中FutureTask和ScheduledFutureTask的netty版本:io.netty.util.concurrent.ScheduledFutureTask,PromiseTask在Netty中的一个应用,下面是SingleThreadEventExecutor启动时会调用的一个函数,该函数将一个周期性的PurgeTask任务包装为一个ScheduledFutureTask对象,添加到延迟任务列表中。
privatevoid startThread() {
synchronized (stateLock) {
if (state ==ST_NOT_STARTED) {
state =ST_STARTED;
delayedTaskQueue.add(new ScheduledFutureTask<Void>(
this,delayedTaskQueue, Executors.<Void>callable(new PurgeTask(),null),
ScheduledFutureTask.deadlineNanos(SCHEDULE_PURGE_INTERVAL), -SCHEDULE_PURGE_INTERVAL));
thread.start();
}
}
}
总结:Netty的Future扩展了JDK的Future接口,提供了异步计算结束后触发操作的定义,而promise再在次之上提供了可以干预异步计算结果的方法,进而提供了更加完善的异步编程接口。
Netty中Future相关角色包括:
Future: 可查询,可触发操作的异步计算结果
Promise:可控的Future
FutureListener:Future触发的操作接口
EventExecutor:执行器
PromiseTask: 包含执行任务的Promise
并提供了包括DefaultPromise,DefaultEventExecutor,PromiseTask等基础实现可以直接使用。
Guava 的Future
Guava 是Goolgle提供了Java编程的基础类库,是编程人推崇备至的神物,有人说从中可以看出编程之美,这下来再慢慢体会吧。
Guava com.google.common.util.concurrent并没有像Netty几乎重写了jdkconcurrent的所有类,而是继承自JDK,添加了监听者的功能。这可以在代码细节上看出两者的一些区别。
Guava com.google.common.util.concurrent.ListenerFuture:
publicinterface ListenableFuture<V>extends Future<V> {
voidaddListener(Runnable listener, Executorexecutor);
}
Guava的Future同样添加了Listener功能,不同的是添加的时候需要传递一个执行该Listener的executor,换句话说所有的Listener可能不是使用一个EventExecutor来执行的。
ListenerFutureTask:
publicclass ListenableFutureTask<V>extends FutureTask<V>
implements ListenableFuture<V> {
privatefinal ExecutionListexecutionList =new ExecutionList();
……
protectedvoid done() {
executionList.execute();
}
}
Guava使用ExecutionList结构来保存FutureListener,当Future任务完成时调用done方法执行所有的Listener.
ExecutionList
publicvoid execute() {
// Lock while we update our state so the add method above will finish adding
// any listeners before we start to run them.
RunnableExecutorPair list;
synchronized (this) {
if (executed) {
return;
}
executed =true;
list =runnables;
runnables =null; // allow GC to free listeners even if this stays around for a while.
}
// If we succeeded then list holds all therunnables we to execute. The pairs in the stack are
// in the opposite order from how they were added so we need to reverse the list to fulfill our
// contract.
// This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we
// could drop the contract on the method that enforces this queue like behavior since depending
// on it is likely to be a bug anyway.
// N.B. All writes to the list and the next pointers must have happened before the above
// synchronized block, so we can iterate the list without the lock held here.
RunnableExecutorPair reversedList = null;
while (list !=null) {
RunnableExecutorPair tmp = list;
list =list.next;
tmp.next =reversedList;
reversedList =tmp;
}
while (reversedList !=null) {
executeListener(reversedList.runnable,reversedList.executor);
reversedList =reversedList.next;
}
}
/**
* Submits the given runnable to the given{@link Executor} catching and logging all
* {@linkplain RuntimeException runtime exceptions} thrown by the executor.
*/
privatestaticvoid executeListener(Runnablerunnable, Executorexecutor) {
try {
executor.execute(runnable);
} catch (RuntimeExceptione) {
// Log it and keep going, bad runnable and/or executor. Don't
// punish the otherrunnables if we're given a bad one. We only
// catch RuntimeException because we want Errors to propagate up.
log.log(Level.SEVERE,"RuntimeException while executing runnable "
+ runnable + " with executor " +executor, e);
}
}
从代码可以看出在最终调用监听者的时候需要同步来获取runnable的监听列表和清空,而Netty把这个同步操作移到了修改Future状态的操作上。
下面再来看下一个重要的类AbstractFuture,该类实现了ListenableFuture,但没有实现Runnable接口,是ListenableFuture实现的推荐基类。
AbstractFuture将Future的状态转换为AQS的state使用AQS来作为Future状态操作的同步器,相比Netty自己实现Future的状态管理,在状态变化中都采用了同步块的方式代码上显得更加简明清晰。(至于效率上没有做压力测试还无法得知)
AbstractFuture部分代码:
/** Synchronization control for AbstractFutures. */
privatefinal Sync<V>sync = new Sync<V>();
……
protectedbooleanset(@Nullable Vvalue) {
booleanresult =sync.set(value);
if (result) {
executionList.execute();
}
returnresult;
}
AbstractFuture.sync部分代码吗:
staticfinalclassSync<V> extends AbstractQueuedSynchronizer {
……
boolean set(@Nullable Vv) {
returncomplete(v,null,COMPLETED);
}
……
privatebooleancomplete(@Nullable Vv, @Nullable Throwablet,
intfinalState) {
booleandoCompletion = compareAndSetState(RUNNING,COMPLETING);
if (doCompletion) {
// If this thread successfully transitioned to COMPLETING, set the value
// and exception and then release to the final state.
this.value =v;
// Don't actually construct a CancellationException until necessary.
this.exception = ((finalState & (CANCELLED |INTERRUPTED)) != 0)
? new CancellationException("Future.cancel() was called.") : t;
releaseShared(finalState);
} elseif (getState() ==COMPLETING) {
// If some other thread is currently completing the future, block until
// they are done so we can guarantee completion.
acquireShared(-1);
}
returndoCompletion;
}
- java Future
- Java --- Future
- java future
- java future
- Java Future接口、Future模式理解
- java Future 接口介绍
- java future模式
- 什么是java future模式
- Java Future接口简介
- JAVA Future类
- java callable和Future
- JAVA中的Future、ExecutorService
- Java Future模式
- 什么是java future模式
- 什么是java future模式
- java Future
- java.util.concurrent.Future
- Java Future/FutureTask
- 融合定位技术(FLP)介绍
- Url转成UTF-8编码
- UESTC 87 Easy Problem With Numbers 线段树区间更新 逆元 分解质因数
- iOS本地通知&定时通知
- TextView使用一些小技巧
- Java --- Future
- swift 根据字符串数量动态计算行高
- 在thinkphp框架既然系统已经有了model为什么还需要创建自己的model
- 控制器的生命周期方法题及内存警告时的处理
- 使用 CAS 在 Tomcat 中实现单点登录
- Mac新升级系统OS X El Capitan实用新功能快速指南
- android开发 百度地图3.0以上版本,显示自定义标记图标
- Android TouchEvent事件传递机制
- 深入理解计算机网络—学习笔记一