什么可以导致同步块内的IllegalMonitorStateException?

时间:2022-11-22 20:58:25

We hit an extremely surprising exception today. Inside of a synchronized block, we call wait() and it throws IllegalMonitorStateException. What can cause this?

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

This is happening in well-tested open source code: http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup#l222

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

We eliminated the obvious causes:

我们消除了明显的原因:

  • are we synchronized on the right variable? Yes, it's muxLock
  • 我们是否在正确的变量上同步?是的,它是muxLock

  • is it a mutable variable? No, muxLock is final
  • 它是一个可变变量吗?不,muxLock是最终的

  • are we using any weird "-XX:" JVM flags that might affect monitor behavior? No, but we are launching the JVM embedded inside a C++ app via JNI.
  • 我们是否正在使用可能影响监视器行为的任何奇怪的“-XX:”JVM标志?不,但我们正在通过JNI启动嵌入在C ++应用程序中的JVM。

  • is this a strange JVM? No, it's Sun's 1.6.0_25 win/x64 JRE
  • 这是一个奇怪的JVM吗?不,这是Sun的1.6.0_25 win / x64 JRE

  • is this a known JVM bug? Can't find anything relevant at http://bugs.sun.com/bugdatabase
  • 这是一个已知的JVM错误吗?在http://bugs.sun.com/bugdatabase上找不到任何相关内容

So, I'm trying to think of more far-fetched explanations.

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

  • could an uncaught out-of-memory error cause the monitor state to be screwed up? We're looking at this, but we're seeing no evidence of memory errors yet.
  • 一个未被发现的内存不足错误会导致监视器状态被搞砸吗?我们正在考虑这个问题,但我们还没有看到内存错误的证据。

UPDATE: (based on comment)

更新:(根据评论)

I've also verified from the stacktrace and breakpoint that the thread is indeed inside the synchronized block when the exception is thrown. It's not the case that some other unrelated code is emitting the exception (unless something is REALLY confusing Eclipse!)

我还从stacktrace和断点验证了当抛出异常时线程确实在synchronized块内。事实并非如此,其他一些不相关的代码会发出异常(除非有些东西真的让Eclipse混乱!)

4 个解决方案

#1


6  

The only suspicious thing I see that you are passing a reference to 'this' to some other object in your constructor. Is it possible (in fact, not unlikely) that, through weird re-ordering of things, if some other thread gets that reference to 'this' and calls the method that uses the muxlock, things can go extremely wrong.

唯一可疑的是我看到你将'this'的引用传递给构造函数中的其他对象。是否有可能(事实上,并非不可能)通过奇怪的重新排序,如果某个其他线程获得对“this”的引用并调用使用muxlock的方法,那么事情就会变得非常错误。

The Java Language Specification is pretty specific about this:

Java语言规范非常具体:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

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

In other words, if another thread gets hold of the 'this' reference before the constructor is finished, the final field 'muxlock' might not be correctly initialized yet. In general, publishing a reference to 'this' before the constructor has finished can be pretty dangerous, especially in threaded situations.

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

Some potentially useful discussion about such things: http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

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

For some older, but still useful general discussion of why publishing 'this' in a constructor is a very bad idea in general, see for instance: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

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

#2


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

here i can see that timeout was added lately

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

make sure that startTimeout is > than 0 otherwise you will wait(0) or wait(-n) this probably cause IllegalMonitorStateException

确保startTimeout>大于0否则你将等待(0)或等待(-n)这可能导致IllegalMonitorStateException

EDIT: Ok above is a disaster But lets try this :

编辑:以上是一场灾难但是让我们试试这个:

we are in Mux constructor : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

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

line 176 we create SocketChannelConnectionIO andd pass this after that we break and and different thread takes over .

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

in constructor of SocketChannelConnectionIO defined here : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup line 112 we register to channel with the new handler().

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

handler recieaves something on chanel and function let say function handleReadReady is executed we synchronize on muxLock .

处理程序收到chanel上的东西和函数让我们说函数handleReadReady执行我们在muxLock上同步。

now we are still in constructor so object in final is still mutable !!! let assume it changes , now we have something waiting on different muxLock

现在我们仍然在构造函数中,所以最终的对象仍然是可变的!让我们假设它发生了变化,现在我们在不同的muxLock上等待着

One in a million scenario

百万分之一的情景

EDIT

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;
    }

now in constructor of SocketChannelConnectionIO http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co

现在在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());
}

move this code to start() should work key = selectionManager.register(channel, new Handler()); (i am assuming start is executet when we want to start prosessing)

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

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

But it would be much better not to create SocketChannelConnectionIO in the constructor of mux but maybe somewhere after that the same for second constructor creating StreamConnectionIO with this

但是最好不要在多路复用器的构造函数中创建SocketChannelConnectionIO,但也许在那之后的某个地方为第二个构造函数创建StreamConnectionIO与此相同

#3


1  

The answer is in my opinion that its either a bug, or someone changed the object behind the reference despite its being final. If you can reproduce it, I recommend to set a read/write breakpoint on muxlock field to see if it is touched or not. You could check the identityhashcode of the muxlock in the first line of the synchronized block, and before waits and notifies with appropiate log entries or breakpoints. With reflection you can change final references. Quote from http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:

答案在我看来,它是一个bug,或者有人改变了引用背后的对象,尽管它是最终的。如果你可以重现它,我建议在muxlock字段上设置一个读/写断点,看它是否被触摸。您可以在同步块的第一行中检查muxlock的identityhashcode,然后等待并通知适当的日志条目或断点。通过反射,您可以更改最终参考。引自http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:

"If the underlying field is final, the method throws an IllegalAccessException unless setAccessible(true) has succeeded for this field and this field is non-static. Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field."

“如果底层字段是final,则该方法抛出IllegalAccessException,除非此字段的setAccessible(true)成功,并且此字段是非静态的。以这种方式设置final字段仅在反序列化或重构类的实例时才有意义空白的最终字段,在它们可供程序的其他部分访问之前。在任何其他上下文中使用可能具有不可预测的影响,包括程序的其他部分继续使用该字段的原始值的情况。

Maybe its a bug in eclispe, and during debugging it somehow changes the field. Is it reproducable outside eclispe as well? Put a printstractrace in catch and see what happens.

也许它是eclispe中的一个错误,在调试过程中它会以某种方式改变字段。它也可以在eclispe之外重现吗?将printstractrace放入catch中,看看会发生什么。

#4


1  

Member variables are not as final as one would hope to. You should put the synchronized object into a final local variable, first. This does not explain why the member variable is altered, but if it fixes the problem you at least know that the member variable is really modified.

成员变量并不像人们希望的那样最终。您应该首先将synchronized对象放入最终的局部变量中。这并不能解释为什么成员变量被改变了,但是如果它解决了问题,你至少知道成员变量真的被修改了。

#1


6  

The only suspicious thing I see that you are passing a reference to 'this' to some other object in your constructor. Is it possible (in fact, not unlikely) that, through weird re-ordering of things, if some other thread gets that reference to 'this' and calls the method that uses the muxlock, things can go extremely wrong.

唯一可疑的是我看到你将'this'的引用传递给构造函数中的其他对象。是否有可能(事实上,并非不可能)通过奇怪的重新排序,如果某个其他线程获得对“this”的引用并调用使用muxlock的方法,那么事情就会变得非常错误。

The Java Language Specification is pretty specific about this:

Java语言规范非常具体:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

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

In other words, if another thread gets hold of the 'this' reference before the constructor is finished, the final field 'muxlock' might not be correctly initialized yet. In general, publishing a reference to 'this' before the constructor has finished can be pretty dangerous, especially in threaded situations.

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

Some potentially useful discussion about such things: http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think

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

For some older, but still useful general discussion of why publishing 'this' in a constructor is a very bad idea in general, see for instance: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

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

#2


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

here i can see that timeout was added lately

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

make sure that startTimeout is > than 0 otherwise you will wait(0) or wait(-n) this probably cause IllegalMonitorStateException

确保startTimeout>大于0否则你将等待(0)或等待(-n)这可能导致IllegalMonitorStateException

EDIT: Ok above is a disaster But lets try this :

编辑:以上是一场灾难但是让我们试试这个:

we are in Mux constructor : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup

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

line 176 we create SocketChannelConnectionIO andd pass this after that we break and and different thread takes over .

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

in constructor of SocketChannelConnectionIO defined here : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup line 112 we register to channel with the new handler().

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

handler recieaves something on chanel and function let say function handleReadReady is executed we synchronize on muxLock .

处理程序收到chanel上的东西和函数让我们说函数handleReadReady执行我们在muxLock上同步。

now we are still in constructor so object in final is still mutable !!! let assume it changes , now we have something waiting on different muxLock

现在我们仍然在构造函数中,所以最终的对象仍然是可变的!让我们假设它发生了变化,现在我们在不同的muxLock上等待着

One in a million scenario

百万分之一的情景

EDIT

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;
    }

now in constructor of SocketChannelConnectionIO http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co

现在在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());
}

move this code to start() should work key = selectionManager.register(channel, new Handler()); (i am assuming start is executet when we want to start prosessing)

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

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

But it would be much better not to create SocketChannelConnectionIO in the constructor of mux but maybe somewhere after that the same for second constructor creating StreamConnectionIO with this

但是最好不要在多路复用器的构造函数中创建SocketChannelConnectionIO,但也许在那之后的某个地方为第二个构造函数创建StreamConnectionIO与此相同

#3


1  

The answer is in my opinion that its either a bug, or someone changed the object behind the reference despite its being final. If you can reproduce it, I recommend to set a read/write breakpoint on muxlock field to see if it is touched or not. You could check the identityhashcode of the muxlock in the first line of the synchronized block, and before waits and notifies with appropiate log entries or breakpoints. With reflection you can change final references. Quote from http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:

答案在我看来,它是一个bug,或者有人改变了引用背后的对象,尽管它是最终的。如果你可以重现它,我建议在muxlock字段上设置一个读/写断点,看它是否被触摸。您可以在同步块的第一行中检查muxlock的identityhashcode,然后等待并通知适当的日志条目或断点。通过反射,您可以更改最终参考。引自http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:

"If the underlying field is final, the method throws an IllegalAccessException unless setAccessible(true) has succeeded for this field and this field is non-static. Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field."

“如果底层字段是final,则该方法抛出IllegalAccessException,除非此字段的setAccessible(true)成功,并且此字段是非静态的。以这种方式设置final字段仅在反序列化或重构类的实例时才有意义空白的最终字段,在它们可供程序的其他部分访问之前。在任何其他上下文中使用可能具有不可预测的影响,包括程序的其他部分继续使用该字段的原始值的情况。

Maybe its a bug in eclispe, and during debugging it somehow changes the field. Is it reproducable outside eclispe as well? Put a printstractrace in catch and see what happens.

也许它是eclispe中的一个错误,在调试过程中它会以某种方式改变字段。它也可以在eclispe之外重现吗?将printstractrace放入catch中,看看会发生什么。

#4


1  

Member variables are not as final as one would hope to. You should put the synchronized object into a final local variable, first. This does not explain why the member variable is altered, but if it fixes the problem you at least know that the member variable is really modified.

成员变量并不像人们希望的那样最终。您应该首先将synchronized对象放入最终的局部变量中。这并不能解释为什么成员变量被改变了,但是如果它解决了问题,你至少知道成员变量真的被修改了。