我是多线程技术的新手,对一个小问题深表歉意。我无法在下面的代码中找到问题所在。我正进入(状态

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),但是然后在该同步部分内,使用baton更改NThread.baton = (NThread.baton+1) % TestUtil.N;对象引用。由于现在在baton中有一个新的对象引用,因此您不再锁定它,因此,当您下次调用baton.notifyAll()时,它位于一个尚未同步的对象上,因此IllegalMonitorStateException

要解决此问题,您需要从触发器(您的final)中分离出同步对象(并使用baton使其不可变-始终是一个好规则)。即只有一个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");
    }
}

通常,这需要进行更多的重构,例如将通用监视器,警棍和参与者数量注入(inject)Runnable的构造函数中,以防止将这些字段公开(通常使用某种自定义类来包含所有字段)。我没有走那么远,所以您可以看到原始代码的链接。

作为单独的脚注,更好的做法是不要重写Threadrun,而是从线程对象中分离出您的操作,然后使ThreadB实现Runnable,然后将其提供给Thread的构造函数。

关于java - baton.notifyAll给出java.lang.IllegalMonitorStateException尽管同步( bat ),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32334368/

10-08 21:50