![](/img/trans.png)
[英]java.lang.IllegalMonitorStateException on notifyAll() method
[英]baton.notifyAll gives java.lang.IllegalMonitorStateException despite synchronized(baton)
我是多線程的新手,對於一個小問題深表歉意。 我在下面的代碼中找不到錯誤。 我正進入(狀態
0-Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
我正在努力實現的目標
我正在嘗試創建一個工作線程數組,每個工作線程將打印為該特定Thread
對象設置的值。 我希望線程等待輪到他們,然后它們將執行代碼並更新baton
值(我懷疑我做錯了),然后通知其他線程,以便周期中的下一個線程將重復執行處理。
這是我的代碼。 謝謝!
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();
}
}
}
}
}
您進行了synchronized (NThread.baton)
,但是隨后在該同步部分內,使用NThread.baton = (NThread.baton+1) % TestUtil.N;
更改了baton
對象引用NThread.baton = (NThread.baton+1) % TestUtil.N;
。 由於您現在在baton
有一個新的對象引用,因此不再需要鎖定它,因此當您下次調用baton.notifyAll()
它位於尚未同步的對象上-因此,您的是IllegalMonitorStateException
。
要解決此問題,您需要從觸發器(您的baton
)中分離出同步對象(並使用final
使它不變-始終是一個好規則)。 即只有一個static final Object monitor = new Object();
您可以進行同步,等待和通知,並保留baton
以用作數字觸發器。
輕微更新的示例是:
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
}
}
}
運行使用:
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");
}
}
通常,這需要進行更多的重構,例如將通用監視器,警棍和參與者數量注入Runnable的構造函數中,以防止將這些字段公開(通常使用某種自定義類來包含所有字段)。 我沒有走那么遠,所以您可以看到原始代碼的鏈接。
作為單獨的腳注,更好的做法是不要重寫Thread
並run
,而是從線程對象中分離出操作,然后使ThreadB
實現Runnable
,然后將其提供給Thread
的構造函數。
您必須了解, synchronized
不會將鎖應用於存儲值的字段 ,而是應用於存儲在該字段中的對象。
要執行wait
或notifyAll
您需要對執行此操作的對象具有監視器鎖定。 我認為您注意到了這個問題,因為您正在使用拳擊類Integer
而不是該字段的int
。 您這樣做是因為synchronized
確實抱怨它無法鎖定值類型。 在增量過程中,將創建一個新的拳擊對象。 並且這個新對象不再被synchronized
塊覆蓋。
在您的情況下,我認為最好的解決方案是使用可用於鎖定的實用程序對象。 任何簡單的Object
實例都可以。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.