[英]baton.notifyAll gives java.lang.IllegalMonitorStateException despite synchronized(baton)
I am new to multi threading and sorry for small question. 我是多线程的新手,对于一个小问题深表歉意。 I could not find what is wrong in below code. 我在下面的代码中找不到错误。 I am getting 我正进入(状态
0-Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
What I'm trying to achieve 我正在努力实现的目标
I am trying to create an array of worker threads, each of which will print the value that is set for that particular Thread
object. 我正在尝试创建一个工作线程数组,每个工作线程将打印为该特定Thread
对象设置的值。 I want the threads to wait for their turn to come and then they will execute the code and update value of baton
(I suspect I am doing this wrong) before notifying the other threads, so that the next thread in the cycle will then repeat the process. 我希望线程等待轮到他们,然后它们将执行代码并更新baton
值(我怀疑我做错了),然后通知其他线程,以便周期中的下一个线程将重复执行处理。
Here is my code. 这是我的代码。 Thanks! 谢谢!
package test;
public class NThread
{
public static Integer baton;
public static void main(String[] args) throws InterruptedException
{
baton = 0;
TestUtil.N = 2;
runThread();
}
protected static void runThread() throws InterruptedException
{
int i;
ThreadB b[] = new ThreadB[TestUtil.N];
for (i = 0; i < TestUtil.N; i++)
{
b[i] = new ThreadB();
b[i].val = i;
b[i].start();
}
}
}
class ThreadB extends Thread
{
public int val;
@Override
public void run()
{
synchronized (NThread.baton)
{
while (true)
{
if (NThread.baton != val)
{
try
{
NThread.baton.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
else
{
TestUtil.printNum(val);
NThread.baton = (NThread.baton+1) % TestUtil.N;
NThread.baton.notifyAll();
}
}
}
}
}
You do synchronized (NThread.baton)
, but then inside that synchronized section you change the baton
object reference using NThread.baton = (NThread.baton+1) % TestUtil.N;
您进行了synchronized (NThread.baton)
,但是随后在该同步部分内,使用NThread.baton = (NThread.baton+1) % TestUtil.N;
更改了baton
对象引用NThread.baton = (NThread.baton+1) % TestUtil.N;
. 。 As you now have a new object reference in baton
you are no longer locking on it, so when you next call baton.notifyAll()
it is on an object you haven't synchronized on - hence your IllegalMonitorStateException
. 由于您现在在baton
有一个新的对象引用,因此不再需要锁定它,因此当您下次调用baton.notifyAll()
它位于尚未同步的对象上-因此,您的是IllegalMonitorStateException
。
To fix this you need to split out your synchronization object (and make it immutable using final
- which is always a good rule anyway) from your trigger (your baton
). 要解决此问题,您需要从触发器(您的baton
)中分离出同步对象(并使用final
使它不变-始终是一个好规则)。 ie have a single static final Object monitor = new Object();
即只有一个static final Object monitor = new Object();
which you synchronize, wait and notify on, and keep baton
for your numeric trigger. 您可以进行同步,等待和通知,并保留baton
以用作数字触发器。
A lightly updated sample is: 轻微更新的示例是:
class ThreadB implements Runnable {
public final int val;
public ThreadB(int val) { this.val = val; }
@Override public void run() {
try {
// synchronize outside the loop so we don't constantly lock/unlock
synchronized (NThread.monitor) {
while (true) { // spin until interrupted
while (NThread.baton != val) // handle spurious wake-ups
NThread.monitor.wait();
// print, increment and notify
TestUtil.printNum(val);
NThread.baton = (NThread.baton + 1) % TestUtil.N;
NThread.monitor.notifyAll();
}
}
} catch (InterruptedException e) {
// if interrupted then we exit
}
}
}
run using: 运行使用:
public class NThread {
public static int baton;
public static final Object monitor = new Object();
public static void main(String[] args) throws InterruptedException {
baton = 0;
TestUtil.N = 2;
runThread();
}
protected static void runThread() throws InterruptedException {
int i;
Thread b[] = new Thread[TestUtil.N];
for (i = 0; i < b.length; i++) { // loop limit is on the array length - its clearer like that
b[i] = new Thread(new ThreadB(i));
b[i].start();
}
TimeUnit.SECONDS.sleep(1);
for (i = 0; i < b.length; i++) b[i].interrupt();
for (i = 0; i < b.length; i++) b[i].join();
System.out.println("All done");
}
}
This would usually undergo a lot more refactoring, such as injecting the common monitor, baton and number of participants into the Runnable's constructor to prevent making those fields public (usually using some kind of custom class to contain them all). 通常,这需要进行更多的重构,例如将通用监视器,警棍和参与者数量注入Runnable的构造函数中,以防止将这些字段公开(通常使用某种自定义类来包含所有字段)。 I didn't go that far so you could see the link to your original code. 我没有走那么远,所以您可以看到原始代码的链接。
As a separate footnote, it is also better practice not to override Thread
and run
, but instead to split out your actions from the threading objects, and so make ThreadB
implement Runnable
, which you then provide to the constructor of a Thread
. 作为单独的脚注,更好的做法是不要重写Thread
并run
,而是从线程对象中分离出操作,然后使ThreadB
实现Runnable
,然后将其提供给Thread
的构造函数。
You have to understand that synchronized
does not apply a lock to the field you are storing a value in, but on the object that is stored in that field. 您必须了解, synchronized
不会将锁应用于存储值的字段 ,而是应用于存储在该字段中的对象。
To execute wait
or notifyAll
you are required to have monitor lock on the object you are executing this for. 要执行wait
或notifyAll
您需要对执行此操作的对象具有监视器锁定。 I think you noticed that problem, because you are using the boxing class Integer
instead of a int
for the field. 我认为您注意到了这个问题,因为您正在使用拳击类Integer
而不是该字段的int
。 You did this because synchronized
did complain that it can't lock on a value type. 您这样做是因为synchronized
确实抱怨它无法锁定值类型。 During the incrementation a new boxing object is created. 在增量过程中,将创建一个新的拳击对象。 And this new object is not any longer covered by the synchronized
block. 并且这个新对象不再被synchronized
块覆盖。
In your case I think the best solution is to use a utility object that you can use for locking. 在您的情况下,我认为最好的解决方案是使用可用于锁定的实用程序对象。 Any simple instance of Object
will do. 任何简单的Object
实例都可以。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.