Java 如何解决代理示例trafficlock和ssl握手锁的死锁?

Java 如何解决代理示例trafficlock和ssl握手锁的死锁?,java,netty,Java,Netty,我将netty示例的大部分合并到了代理中(http://netty.io/docs/stable/xref/org/jboss/netty/example/proxy/)和安全聊天(http://netty.io/docs/stable/xref/org/jboss/netty/example/securechat)形成支持SSL(和非SSL)的代理(到非SSL后端)。这似乎是一个好主意,被证明足够简单,这正是我目前所需要的 代理示例代码使用trafficlock锁作为2010年报告的竞争条件的

我将netty示例的大部分合并到了代理中(http://netty.io/docs/stable/xref/org/jboss/netty/example/proxy/)和安全聊天(http://netty.io/docs/stable/xref/org/jboss/netty/example/securechat)形成支持SSL(和非SSL)的代理(到非SSL后端)。这似乎是一个好主意,被证明足够简单,这正是我目前所需要的

代理示例代码使用trafficlock锁作为2010年报告的竞争条件的解决方案(http://markmail.org/message/x7jc6mqx6ripynqf)发生在饱和通道周围,并设置传入和传出通道的可写和可读状态

现在,在我的组合示例中,在更高的负载下,这会导致死锁,因为SSL代码中的另一个锁“握手锁”相互交织。请参阅下面的探查器诊断输出

我担心,即使在阅读了最初的讨论之后,我也不太理解流量锁的根本问题,无法找到解决此死锁的简单方法

(这是与netty 3.2.6一起发布的)

已检测到Java级死锁 这意味着某些线程在等待进入同步块或同步时被阻塞 等待在Object.wait()调用后重新输入同步块,其中每个线程 在尝试获取另一个线程已持有的另一个监视器时拥有一个监视器。 死锁: 新的I/O客户端工作程序#1-2正在等待锁定java.lang。Object@7900f3c9由新的I/O服务器工作者#1-2持有 新的I/O服务器工作程序#1-2正在等待锁定java.lang。Object@2d854f2f由新的I/O客户端工作人员#1-2持有 线程堆栈 新的I/O客户端工作程序#1-2[被阻止;等待锁定java.lang。Object@7900f3c9]
org.jboss.netty.handler.ssl.SslHandler.wrap(SslHandler.java:665)好的,我想我已经解决了

代理示例中早期竞争条件的解决方案不必要地在synchronized块中包含对write()的调用

最初的问题是传出线程(TO)和传入线程(TI)之间存在这种(罕见的)争用情况:

  • 收件人:inboundChannel.write()(在messageReceived处)
  • TO:inboundChannel.isWritable()返回false(在messageReceived处)
  • 然后刷新在(1)处发出的挂起写入
  • TI:inboundChannel.isWritable()返回true(在channelInterestChanged处)
  • TI:outboundChannel.setReadable(true)(在channelInterestChanged时)
  • TO:outboundChannel.setReadable(false)(在messageReceived时)
  • 2010年的解决方案是围绕在inboundChannel和outboundChannel的messageReceived()处理程序(以及InterestChanged处理程序)中设置可读标志引入同步(使用“trafficlock”),如下所示:

    synchronized (trafficLock) {
      outboundChannel.write(msg);
      // If outboundChannel is saturated, do not read until notified in
      // OutboundHandler.channelInterestChanged().
      if (!outboundChannel.isWritable()) {
        e.getChannel().setReadable(false);
      }
    }
    
    这确实解决了竞争条件,因为您可以防止步骤3、4和5干扰步骤2和6。但是,将步骤1 write()从同步块中保留是安全的

    对write()的调用导致了死锁,因为在SSLHandler的调用之后,它使用了另一个锁握手锁


    所以。。我将对write()的调用移到两个位置的synchronized块之外。僵局现在已经过去。我建议相应地更改“官方”代理示例。

    顺便说一句,3.2.6已经很旧了。。您应该升级到3.4.0.Final
    synchronized (trafficLock) {
      outboundChannel.write(msg);
      // If outboundChannel is saturated, do not read until notified in
      // OutboundHandler.channelInterestChanged().
      if (!outboundChannel.isWritable()) {
        e.getChannel().setReadable(false);
      }
    }