簡體   English   中英

baton.notifyAll給出java.lang.IllegalMonitorStateException盡管同步(蝙蝠)

[英]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的構造函數中,以防止將這些字段公開(通常使用某種自定義類來包含所有字段)。 我沒有走那么遠,所以您可以看到原始代碼的鏈接。

作為單獨的腳注,更好的做法是不要重寫Threadrun ,而是從線程對象中分離出操作,然后使ThreadB實現Runnable ,然后將其提供給Thread的構造函數。

您必須了解, synchronized不會將鎖應用於存儲值的字段 ,而是應用於存儲在該字段中的對象。

要執行waitnotifyAll您需要對執行此操作的對象具有監視器鎖定。 我認為您注意到了這個問題,因為您正在使用拳擊類Integer而不是該字段的int 您這樣做是因為synchronized確實抱怨它無法鎖定值類型。 在增量過程中,將創建一個新的拳擊對象。 並且這個新對象不再被synchronized塊覆蓋。

在您的情況下,我認為最好的解決方案是使用可用於鎖定的實用程序對象。 任何簡單的Object實例都可以。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM