Làm cách nào để sử dụng chờ và thông báo trong Java mà không cần IllegalMonitorStateException?


129

Tôi có 2 ma trận và tôi cần nhân chúng và sau đó in kết quả của từng ô. Ngay khi một ô sẵn sàng, tôi cần in nó, nhưng ví dụ tôi cần in ô [0] [0] trước ô [2] [0] ngay cả khi kết quả của [2] [0] đã sẵn sàng trước . Vì vậy, tôi cần phải in nó theo thứ tự. Vì vậy, ý tưởng của tôi là làm cho luồng máy in chờ cho đến khi multiplyThreadthông báo rằng ô chính xác đã sẵn sàng để in và sau đó printerThreadsẽ in ô và quay lại chờ và cứ thế ..

Vì vậy, tôi có chủ đề này làm phép nhân:

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();
}

Chủ đề in kết quả của mỗi ô:

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] + " ");
            }
        }
    }

Bây giờ nó ném cho tôi những ngoại lệ:

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)

dòng 49 in multiplyThreadlà "notify ()" .. Tôi nghĩ rằng tôi cần sử dụng đồng bộ hóa khác nhau nhưng tôi không chắc làm thế nào.

Nếu bất cứ ai có thể giúp mã này hoạt động tôi sẽ thực sự đánh giá cao nó.

Câu trả lời:


215

Để có thể gọi thông báo () bạn cần đồng bộ hóa trên cùng một đối tượng.

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

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

29
Các while(!JobCompleted);tùy chọn nói chung là một ý tưởng tồi vì nó gắn lên CPU của bạn ở mức 100% kiểm tra cùng một biến liên tục (xem ở đây )
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); không có vấn đề đó
BeniBela

15
Nó vẫn có vấn đề là một cái gì đó hoàn toàn khác. Bỏ phiếu (kiểm tra nhiều lần nếu một số điều kiện được đáp ứng, tức là những gì bạn đang làm) thường ít được ưu tiên hơn so với việc được thông báo nếu điều kiện nói được thay đổi (nghĩa là những gì tôi đã nêu trong câu trả lời).
Bombe

3
@huseyintugrulbuyukisik bạn có thể gọi waitbất cứ khi nào chủ đề hiện tại có khóa trên đối tượng waitđược gọi. Cho dù bạn sử dụng một synchronizedkhối hoặc một phương thức đồng bộ hóa là hoàn toàn tùy thuộc vào bạn.
Bombe

1
@BeniBela Nhưng chắc chắn sẽ chậm hơn (liên quan đến câu hỏi ban đầu của Huseyin).
Thomas

64

Trong khi sử dụng các phương thức waitnotifyhoặc notifyAlltrong Java, những điều sau đây phải được ghi nhớ:

  1. Sử dụng notifyAllthay vì notifynếu bạn mong đợi rằng nhiều hơn một luồng sẽ chờ khóa.
  2. Các waitnotify phương thức và phải được gọi trong một bối cảnh đồng bộ . Xem liên kết để được giải thích chi tiết hơn.
  3. Luôn gọi cho wait() phương thức trong một vòng lặp bởi vì nếu nhiều luồng đang chờ khóa và một trong số chúng có khóa và đặt lại điều kiện, thì các luồng khác cần kiểm tra điều kiện sau khi chúng thức dậy để xem liệu chúng có cần đợi lại hay không có thể bắt đầu xử lý.
  4. Sử dụng cùng một đối tượng để gọi wait()notify()phương thức; mỗi đối tượng đều có khóa riêng nên việc gọi wait()đối tượng A và notify()đối tượng B sẽ không có ý nghĩa gì.

21

Bạn có cần phải luồng này không? Tôi đang tự hỏi ma trận của bạn lớn đến mức nào, và liệu có lợi ích gì khi có một luồng in trong khi cái kia thực hiện phép nhân.

Có lẽ sẽ đáng để đo thời gian này trước khi thực hiện công việc phân luồng tương đối phức tạp?

Nếu bạn cần xâu chuỗi nó, tôi sẽ tạo các luồng 'n' để thực hiện phép nhân của các ô (có lẽ 'n' là số lõi có sẵn cho bạn), sau đó sử dụng cơ chế ExecutorServiceFuture để gửi nhiều lần .

Bằng cách đó, bạn có thể tối ưu hóa công việc dựa trên số lượng lõi và bạn đang sử dụng các công cụ phân luồng Java cấp cao hơn (sẽ giúp cuộc sống dễ dàng hơn). Viết kết quả trở lại vào một ma trận nhận và sau đó chỉ cần in nó sau khi tất cả các nhiệm vụ Tương lai của bạn đã hoàn thành.


1
+1 @Greg Tôi nghĩ bạn nên xem qua gói java.util.conc hiện, như được chỉ ra bởi Brian.
ATorras

1
+1 và cũng xem cuốn sách này cũng sẽ dạy cho bạn cách sử dụng chính xác Wait
Chii

14

Giả sử bạn có ứng dụng 'hộp đen' với một số lớp có tên BlackBoxClasslà phương thứcdoSomething(); .

Hơn nữa, bạn có người quan sát hoặc người nghe được đặt tên onResponse(String resp)sẽ được gọi bởiBlackBoxClass sau thời gian không xác định.

Dòng chảy rất đơn giản:

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

Hãy nói rằng chúng tôi không biết chuyện gì đang xảy ra BlackBoxClassvà khi nào chúng tôi sẽ nhận được câu trả lời nhưng bạn không muốn tiếp tục mã của mình cho đến khi bạn nhận được câu trả lời hoặc nói cách khác là nhận onResponsecuộc gọi. Ở đây nhập 'Đồng bộ hóa trình trợ giúp':

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) {
        }
    }
}
}

Bây giờ chúng tôi có thể thực hiện những gì chúng tôi muốn:

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}

7

Bạn chỉ có thể gọi thông báo trên các đối tượng mà bạn sở hữu màn hình của họ. Vì vậy, bạn cần một cái gì đó như

synchronized(threadObject)
{
   threadObject.notify();
}


3

Tôi sẽ ví dụ đơn giản chỉ cho bạn cách sử dụng đúng waitnotifytrong Java. Vì vậy, tôi sẽ tạo hai lớp có tên ThreadA & ThreadB . ThreadA sẽ gọi 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);
        }
    }
} 

và cho Class 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
            }
        }
    }

3

Sử dụng đơn giản nếu bạn muốn Cách thực hiện các luồng thay thế: -

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();
    }
}

phản ứng :-

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

Làm thế nào để nó hoạt động khi tôi có 4 thao tác để thực hiện một cách đồng bộ?
saksham agarwal

2

chúng ta có thể gọi thông báo để tiếp tục thực hiện các đối tượng đang chờ

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

tiếp tục điều này bằng cách gọi thông báo về một đối tượng khác cùng lớp

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

0

Đối với vấn đề cụ thể này, tại sao không lưu trữ các kết quả khác nhau của bạn trong các biến và sau đó khi xử lý cuối cùng của chuỗi bạn có thể in ở bất kỳ định dạng nào bạn muốn. Điều này đặc biệt hữu ích nếu bạn sẽ sử dụng lịch sử công việc của mình trong các dự án khác.


0

Điều này trông giống như một tình huống cho mô hình sản xuất-người tiêu dùng. Nếu bạn đang sử dụng java 5 trở lên, bạn có thể xem xét sử dụng hàng đợi chặn (java.util.concản.BlockingQueue) và để công việc phối hợp luồng cho việc triển khai khung / api cơ bản. Xem ví dụ từ java 5: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concien/BlockingQueue.html hoặc java 7 (ví dụ tương tự): http: // docs. oracle.com/javase/7/docs/api/java/util/concản/BlockingQueue.html


0

Bạn đã bảo vệ đúng khối mã của mình khi bạn gọi wait()phương thức bằng cách sử dụngsynchronized(this) .

Nhưng bạn đã không thực hiện biện pháp phòng ngừa tương tự khi bạn gọi notify()phương thức mà không sử dụng khối được bảo vệ:synchronized(this) hoặcsynchronized(someObject)

Nếu bạn tham khảo trang tài liệu oracle trên Object lớp, trong đó có wait(), notify(), notifyAll()phương pháp, bạn có thể xem dưới đây để đề phòng trong cả ba phương pháp này

Phương thức này chỉ được gọi bởi một chủ đề là chủ sở hữu màn hình của đối tượng này

Nhiều thứ đã được thay đổi trong 7 năm qua và chúng ta hãy xem xét các lựa chọn thay thế khác để synchronized trong các câu hỏi SE dưới đây:

Tại sao nên sử dụng ReentrantLock nếu người ta có thể sử dụng đồng bộ hóa (cái này)?

Đồng bộ hóa với Khóa

Tránh đồng bộ hóa (cái này) trong Java?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.