简体   繁体   English

baton.notifyAll给出java.lang.IllegalMonitorStateException尽管同步(蝙蝠)

[英]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 . 作为单独的脚注,更好的做法是不要重写Threadrun ,而是从线程对象中分离出操作,然后使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. 要执行waitnotifyAll您需要对执行此操作的对象具有监视器锁定。 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.

相关问题 notifyAll()方法上的java.lang.IllegalMonitorStateException - java.lang.IllegalMonitorStateException on notifyAll() method 如何在不获取java.lang.IllegalMonitorStateException的情况下更改在同步块中获取的锁,对其进行更改以及notifyAll()? - how can I change the lock I have aquired in a synchronized block, change it, and notifyAll() without getting java.lang.IllegalMonitorStateException? 从同步块中调用wait时发生java.lang.IllegalMonitorStateException - java.lang.IllegalMonitorStateException whilst calling wait from synchronized block 超时时发生java.lang.IllegalMonitorStateException - java.lang.IllegalMonitorStateException on timeout 当我以静态方式同步块调用wait()时,为什么Java会抛出java.lang.IllegalMonitorStateException? - Why Java throw java.lang.IllegalMonitorStateException when I invoke wait() in static way synchronized block? java.lang.IllegalMonitorStateException (java selenium) - java.lang.IllegalMonitorStateException (java selenium) 通知异常 java.lang.IllegalMonitorStateException Locks - Notify exception java.lang.IllegalMonitorStateException Locks 线程“main”java.lang.IllegalMonitorStateException中的异常 - Exception in thread “main” java.lang.IllegalMonitorStateException 我的代码抛出java.lang.IllegalMonitorStateException - my code throws java.lang.IllegalMonitorStateException 导致java.lang.IllegalMonitorStateException的原因 - What causes an java.lang.IllegalMonitorStateException
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM