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,然后抛出异常避免死锁。