如何在没有IllegalMonitorStateException的Java中使用wait和notify?

时间:2009-05-20 08:04:54

标签: java multithreading exception java-threads

我有2个矩阵,我需要将它们相乘,然后打印每个单元格的结果。一旦一个单元格准备就绪,我需要打印它,但是例如我需要在单元格[2] [0]之前打印[0] [0]单元格,即使[2] [0]的结果已经准备好了。所以我需要按顺序打印它。 所以我的想法是让打印机线程等待,直到multiplyThread通知它正确的单元格已准备好打印,然后printerThread将打印单元格并返回等待等等。

所以我有这个线程进行乘法运算:

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

打印每个单元格结果的线程:

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

现在它抛出了这些例外:

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

multiplyThread中的第49行是“notify()”..我认为我需要使用不同的同步,但我不确定如何。

如果有人可以帮助这些代码工作,我将非常感激。

12 个答案:

答案 0 :(得分:209)

为了能够呼叫notify(),您需要在同一个对象上进行同步。

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}

答案 1 :(得分:63)

在Java中使用waitnotifynotifyAll方法时,必须记住以下内容:

  1. 如果您希望多个线程等待锁定,请使用notifyAll而不是notify
  2. The wait and notify methods must be called in a synchronized context。请参阅链接以获取更详细的说明。
  3. 始终在循环中调用wait()方法,因为如果多个线程正在等待锁定并且其中一个线程获得锁定并重置条件,那么其他线程需要在唤醒之后检查条件看他们是否需要再次等待或者可以开始处理。
  4. 使用相同的对象来调用wait()notify()方法;每个对象都有自己的锁,因此调用对象A上的wait()和对象B上的notify()将没有任何意义。

答案 2 :(得分:20)

你需要解决这个问题吗?我想知道你的矩阵有多大,一个线程打印是否有任何好处,另一个是乘法。

在进行相对复杂的线程工作之前,或许值得测量这段时间?

如果你确实需要线程化,我会创建'n'个线程来执行单元格的乘法运算(也许'n'是可用的核心数),然后使用ExecutorServiceFuture机制同时分派多个乘法。

通过这种方式,您可以根据内核数量优化工作,并且您正在使用更高级别的Java线程工具(这应该会让生活变得更轻松)。将结果写回接收矩阵,然后在完成所有Future任务后立即打印。

答案 3 :(得分:13)

假设您有一个“黑匣子”应用程序,其中一个名为BlackBoxClass的类具有方法doSomething();

此外,您有一个名为onResponse(String resp)的观察者或听众,将在{1}}被未知时间后调用。

流程很简单:

BlackBoxClass

让我们说我们不知道private String mResponse = null; ... BlackBoxClass bbc = new BlackBoxClass(); bbc.doSomething(); ... @override public void onResponse(String resp){ mResponse = resp; } 发生了什么,当我们得到答案但你不想继续你的代码,直到你得到答案或换句话说得到BlackBoxClass呼叫。这里输入'Synchronize helper':

onResponse

现在我们可以实现我们想要的东西:

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

答案 4 :(得分:7)

您只能在拥有其监视器的对象上调用notify。所以你需要像

这样的东西
synchronized(threadObject)
{
   threadObject.notify();
}

答案 5 :(得分:6)

notify()也需要同步

答案 6 :(得分:3)

我正确的简单示例向您展示了在Java中使用waitnotify的正确方法。 因此,我将创建两个名为 ThreadA &amp;的类。的 ThreadB 即可。 ThreadA将调用ThreadB。

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

和类ThreadB:

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }

答案 7 :(得分:2)

如果需要,可以简单地使用如何交替执行线程: -

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}
  

回复: -

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

答案 8 :(得分:2)

我们可以调用notify来恢复等待对象的执行

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

通过在同一个类的另一个对象上调用notify来恢复这个

// Create a listener.
        HttpListener listener = new HttpListener();
        // Add the prefixes.
        //foreach (string s in prefixes)
        //{
        //    listener.Prefixes.Add(s);
        //}
        listener.Prefixes.Add("http://*:1234/"); // accept connections from everywhere,
        //because the printer is accessible only within the LAN (no portforwarding)
        listener.Start();
        Console.WriteLine("Listening...");
        // Note: The GetContext method blocks while waiting for a request. 
        HttpListenerContext context;
        string urlForRequest = "";

        HttpWebRequest requestForPage = null;
        HttpWebResponse responseForPage = null;
        string responseForPageAsString = "";

        while (true)
        {
            context = listener.GetContext();
            HttpListenerRequest request = context.Request;
            urlForRequest = request.RawUrl.Substring(1, request.RawUrl.Length - 1); // remove the slash, which separates the portNumber from the arg sent
            Console.WriteLine(urlForRequest);

            //Request for the html page:
            requestForPage = (HttpWebRequest)WebRequest.Create(urlForRequest);
            responseForPage = (HttpWebResponse)requestForPage.GetResponse();
            responseForPageAsString = new StreamReader(responseForPage.GetResponseStream()).ReadToEnd();

            // Obtain a response object.
            HttpListenerResponse response = context.Response;
            // Send back the response.
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseForPageAsString);
            // Get a response stream and write the response to it.
            response.ContentLength64 = buffer.Length;
            response.AddHeader("Access-Control-Allow-Origin", "*"); // the magic header in action ;-D
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            // You must close the output stream.
            output.Close();
            //listener.Stop();

答案 9 :(得分:0)

对于这个特殊问题,为什么不将各种结果存储在变量中,然后在处理最后一个线程时,您可以以任何您想要的格式打印。如果您要在其他项目中使用您的工作历史,这将特别有用。

答案 10 :(得分:0)

使用wait()调用synchronized(this)方法时,您已正确保护代码块。

但是,如果您在不使用防护阻止的情况下调用notify()方法,则未采取相同的预防措施:synchronized(this)synchronized(someObject)

如果您参考Object上的oracle文档页面,其中包含wait()notify()notifyAll()方法,您可以在以下三种方法中看到以下预防措施< / p>

  

此方法只应由作为此对象监视器所有者的线程调用

在过去7年中,许多事情发生了变化,让我们在以下SE问题中研究synchronized的其他替代方案:

Why use a ReentrantLock if one can use synchronized(this)?

Synchronization vs Lock

Avoid synchronized(this) in Java?

答案 11 :(得分:0)

这看起来像生产者 - 消费者模式的情况。如果您正在使用java 5或更高版本,您可以考虑使用阻塞队列(java.util.concurrent.BlockingQueue)并将线程协调工作留给底层框架/ api实现。 请参阅示例 java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html 或java 7(相同示例): http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html