[英]Why does this throw an IllegalMonitorStateException?
I am getting into Java multithreading. 我正在学习Java多线程。 I am very familiar with C/C++ pthreads, but am having issues with the Java notify()
and wait()
functions. 我对C / C ++ pthread非常熟悉,但是Java的notify()
和wait()
函数存在问题。
I understand that an IllegalMoinitorStateException
is only thrown when a thread that doesnt "own" (aka hasnt synchronized) calls notify/wait. 我了解,仅当不“拥有”(也称为“未同步”)的线程调用通知/等待时,才会引发IllegalMoinitorStateException
。
When writing my application, I ran into this issue. 在编写应用程序时,我遇到了这个问题。 I isolated the problem with the following test code: 我使用以下测试代码隔离了该问题:
public class HelloWorld
{
public static Integer notifier = 0;
public static void main(String[] args){
notifier = 100;
Thread thread = new Thread(new Runnable(){
public void run(){
synchronized (notifier){
System.out.println("Notifier is: " + notifier + " waiting");
try{
notifier.wait();
System.out.println("Awake, notifier is " + notifier);
}
catch (InterruptedException e){e.printStackTrace();}
}
}});
thread.start();
try{
Thread.sleep(1000);
}
catch (InterruptedException e){
e.printStackTrace();
}
synchronized (notifier){
notifier = 50;
System.out.println("Notifier is: " + notifier + " notifying");
notifier.notify();
}
}
}
This outputs: 输出:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at HelloWorld.main(HelloWorld.java:27)
I believe I have already acquired the lock on the notifier object. 我相信我已经获得了通知程序对象的锁定。 What am I doing wrong? 我究竟做错了什么?
Thanks! 谢谢!
EDIT: 编辑:
From this possible duplicate( Synchronizing on an Integer value ), it seems that it is not a good idea to synchronize on an Integer because it is hard to make sure you are synchronizing on the same instance. 从这个可能的重复项( 在Integer值上同步 )看来,在Integer上同步并不是一个好主意,因为很难确保您在同一实例上进行同步。 Since my integer I am synchronizing on is a global visibile static integer, why am I getting different instances? 由于我要同步的整数是全局可见的静态整数,为什么会得到不同的实例?
Because of notifier = 50;
因为notifier = 50;
you are calling notifier.notify();
您正在调用notifier.notify();
on a different object. 在另一个对象上。
Initially when you calling synchronized on notifier
in non-main Thread it was referencing to the object on heap whose content was 0
hence the thread owns that object . 最初,当您在非主线程中的notifier
上调用synchronized时,它引用的是内容为0
堆上的对象,因此线程拥有该对象。 Now after non-main thread is put on wait
using notifier.wait
the control is given to the main thread . 现在,使用notifier.wait
将非主线程置于wait
将控制权交给主线程。 There after acquiring lock on object of Integer containing value 0 you made the notifier
to refer the other object on heap memory containing value 50
because notifier = 50;
在获得对包含值0的Integer对象的锁定之后,您使notifier
程序引用包含值50
堆存储器上的另一个对象,因为notifier = 50;
is actually equivalent to notifier = new Integer(50)
which means a new object of Integer
is created and its reference is passed to notifier
. 实际上等效于notifier = new Integer(50)
,这意味着将创建一个Integer
的新对象,并将其引用传递给notifier
。 Now , when the thread sees notifier.notify
it looks that the Main thread now no longer owns the original object that it had acquired before. 现在,当线程看到notifier.notify
它看起来像是Main线程现在不再拥有它之前获取的原始对象。 So IllegalMonitorStateException
is throwing. 因此,抛出IllegalMonitorStateException
。
Just to add more information, you should never synchronize on a non-final object. 仅添加更多信息,就永远不要在非最终对象上进行同步。
You need to synchronize on a constant object. 您需要在一个常量对象上进行同步。 If you synchronized on any object that you are assigning (ie changing the object) then the object is not constant and once it assigns the object and tries to notify on it, you will get the IllegalMonitorStateException
. 如果您在要分配的任何对象上进行同步(即更改对象),则该对象不是恒定的,并且一旦分配了对象并尝试对其进行通知,您将获得IllegalMonitorStateException
。 It also means that because they are synchronizing on different objects, multiple threads will be entering the protected block at the same time and race conditions will happen. 这也意味着,由于它们正在不同的对象上进行同步,因此多个线程将同时进入受保护的块,并且会发生竞争状况。
Since my integer I am synchronizing on is a global visibile static integer, why am I getting different instances? 由于我要同步的整数是全局可见的静态整数,为什么会得到不同的实例?
When you assign a value to an Integer
it changes it to a different object reference -- even if it is static
. 将值分配给Integer
,即使它是static
它也会将其更改为其他对象引用。 You are not changing the same object because Integer
can't be mutated. 您不会更改同一对象,因为不能对Integer
进行突变。 So notifier = 50;
notifier = 50;
assigns it to a different object than notifier = 0;
将其分配给notifier = 0;
以外的其他对象notifier = 0;
. 。
If you want to use, for example, a constant object you can use an AtomicBoolean
: 例如,如果要使用常量对象,则可以使用AtomicBoolean
:
public static final AtomicInteger notifier = new AtomicInteger(0);
...
synchronize (notifier) {
notifier.set(50);
...
}
Here, AtomicInteger
can be marked final
because it's the same object all of the time. 在这里,可以将AtomicInteger
标记为final
因为它始终都是同一对象。
For more information, see: Why is it not a good practice to synchronize on Boolean? 有关更多信息,请参见: 为什么在Boolean上同步不是一个好习惯?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.