Future机制用于并发编程时的死锁检测
Netty源码里面有个类:DeadLockProofWorker,源码如下:
public static final ThreadLocal<Executor> PARENT = new ThreadLocal<Executor>(); public static void start(final Executor parent, final Runnable runnable) { if (parent == null) { throw new NullPointerException("parent"); } if (runnable == null) { throw new NullPointerException("runnable"); } parent.execute(new Runnable() { public void run() { PARENT.set(parent); try { runnable.run(); } finally { PARENT.remove(); } } }); } private DeadLockProofWorker() { super(); }
假设有下面的代码:
ChannelFuture f = AChannelHandlerContext.getChannel().write(res); f.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { future.await();//(1)Thread 1 waiting for itself to be done. //ChannelFuture f is done only if this invoke returns. }); f.await();//(2)Thead 2 wait for f to be done.
如果ChannelFuture没有死锁检测,那么这两个线程将永远处于死锁状态。
Thread2 等待ChannelFuture f 完成,ChannelFuture f 必须把listener的代码跑完才会完成,而listener也在等待future完成,于是死锁就造成了。
看如何用线程变量杜绝死锁。
上述代码: AChannelHandlerContext.getChannel().write(res) 会把写的操作放到线程池里异步进行,并且是这样放进去的:
DeadLockProofWorker.start(executor, ARunnable r).
上面的代码在r运行的线程里面可以通过DeadLockProofWorker.PARENT.get()获取到executor,即一个非null的对象,如果在f.await()里面检测一下,就可以知道await是否在r的线程里面了:
public ChannelFuture await() throws InterruptedException { ... checkDeadLock(); ... return this; } private static void checkDeadLock() { if (DeadLockProofWorker.PARENT.get() != null) { throw new IllegalStateException( "await*() in I/O thread causes a dead lock or " + "sudden performance drop. Use addListener() instead or " + "call await*() from a different thread."); } }
如果DeadLockProofWorker.PARENT.get() != null 成立,那么当前线程就是启动线程1,然后抛出异常避免死锁。