什么会导致非法监控状态从同步块内部出现?

我们今天遇到了一个非常令人惊讶的例外。在同步块内部,我们调用 wait() 并抛出 .什么原因导致这种情况?IllegalMonitorStateException

这发生在经过充分测试的开源代码中:http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup#l222

我们消除了明显的原因:

  • 我们是否在正确的变量上同步?是的,它是muxLock
  • 它是可变变量吗?否,是最终的muxLock
  • 我们是否使用了任何可能影响监视器行为的奇怪的“-XX:”JVM 标志?不,但是我们正在通过JNI启动嵌入在C++应用程序中的JVM。
  • 这是一个奇怪的JVM吗?不,这是太阳的1.6.0_25赢/x64 JRE
  • 这是一个已知的JVM错误吗?在 http://bugs.sun.com/bugdatabase 找不到任何相关内容

所以,我试图想出更牵强的解释。

  • 未捕获的内存不足错误是否会导致显示器状态搞砸?我们正在研究这个问题,但我们还没有看到内存错误的证据。

更新:(基于评论)

我还从堆栈跟踪和断点验证了当引发异常时,线程确实在同步块内。这并不是说其他一些不相关的代码会发出异常(除非有些东西真的令人困惑的Eclipse!


答案 1

我看到的唯一可疑的事情是,您正在将对“this”的引用传递给构造函数中的其他对象。有没有可能(事实上,并非不太可能)通过奇怪的事物重新排序,如果其他线程获得对“this”的引用并调用使用复用锁的方法,事情可能会变得非常错误。

Java语言规范对此非常具体:

当对象的构造函数完成时,将被视为已完全初始化。如果线程只能在对象完全初始化后才能看到对该对象的引用,则保证可以看到该对象的最终字段的正确初始化值。

换句话说,如果另一个线程在构造函数完成之前获得了“this”引用,则最终字段“muxlock”可能尚未正确初始化。通常,在构造函数完成之前发布对“this”的引用可能非常危险,尤其是在线程化情况下。

关于这些事情的一些潜在的有用讨论:http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

对于一些较旧的,但仍然有用的一般讨论,为什么在构造函数中发布“this”通常是一个非常糟糕的主意,例如,请参阅:http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html


答案 2

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?r1=1069292&r2=1135026&diff_format=h

在这里我可以看到最近添加了超时

确保 startTimeout >小于 0,否则您将 wait(0) 或 wait(-n),这可能会导致 IllegalMonitorStateException

编辑:好的上面是一场灾难,但让我们试试这个:

我们在Mux构造函数中:http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

第176行 我们创建SocketChannelConnectionIO并传递它之后,我们中断并和不同的线程接管。

在 SocketChannelConnectionIO 的构造函数中,定义在这里:http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup 行 112,我们使用新的 handler() 注册到通道。

处理程序在香奈儿和函数上接收一些东西,比如说函数句柄ReadReady被执行,我们在muxLock上同步。

现在我们仍然在构造函数中,所以最终的对象仍然是可变的!!!让我们假设它改变了,现在我们有一些东西在等待不同的muxLock

百万分之一的场景

编辑

http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?revision=1135026&view=co

Mux(SocketChannel channel,
    int role, int initialInboundRation, int maxFragmentSize)
    throws IOException
    {
    this.role = role;
    if ((initialInboundRation & ~0x00FFFF00) != 0) {
        throw new IllegalArgumentException(
        "illegal initial inbound ration: " +
        toHexString(initialInboundRation));
    }
    this.initialInboundRation = initialInboundRation;
    this.maxFragmentSize = maxFragmentSize;

    //LINE BELOW IS CAUSING PROBLEM it passes this to SocketChannelConnectionIO
    this.connectionIO = new SocketChannelConnectionIO(this, channel);

    //Lets assume it stops here we are still in constructor
    //and we are not in synchronized block

    directBuffersUseful = true;
    }

现在在 SocketChannelConnectionIO 的构造函数中,http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co

SocketChannelConnectionIO(Mux mux, SocketChannel channel)
    throws IOException
{
    super(mux);
    channel.configureBlocking(false);
    this.channel = channel;
    //Line below we are registering to the channel with mux that is still mutable
    //this is the line that actually is causing the problem move that to 
    // start() and it should work 
    key = selectionManager.register(channel, new Handler());
}

将此代码移动到start()应该工作(我假设当我们想要开始散文时,start被执行)key = selectionManager.register(channel, new Handler());

/**
 * Starts processing connection data.
 */
void start() throws IOException {
    key = selectionManager.register(channel, new Handler());
    key.renewInterestMask(SelectionKey.OP_READ);
}

但是,最好不要在mux的构造器中创建SocketChannelConnectionIO,但也许在那之后的某个地方,对于用这个创建StreamConnectionIO的第二个构造函数来说也是如此。


推荐