wait () 호출시 IllegalMonitorStateException


162

내 프로그램에 Java에서 멀티 스레딩을 사용하고 있습니다. 스레드를 성공적으로 실행했지만 사용할 때 Thread.wait()던지고 java.lang.IllegalMonitorStateException있습니다. 스레드가 알림을받을 때까지 기다리려면 어떻게해야합니까?


2
Thread.wait ()가 존재하지 않습니다. this.wait () 일 수 있습니다.
Premraj

답변:


175

당신이 일 synchronized하기 위해서는 블록에 있어야 Object.wait()합니다.

또한 구식 스레딩 패키지 대신 동시성 패키지를 확인하는 것이 좋습니다. 더 안전하고 작업하기 쉽습니다 .

행복한 코딩.

편집하다

Object.wait()객체 잠금을 유지하지 않고 액세스하려고 할 때 예외가 발생 한다는 것을 의미한다고 가정했습니다 .


1
잘 잡는다. 나는 그가로 Object.wait ()를 의미하고 스레드에서 호출 가정
reccles

2
대기중인 객체의 동기화 된 블록입니다. 좀 더 명확하게하기 위해이 답변을 편집 하시겠습니까? 감사.
Grey

55

wait에 정의되어 Object있지만 아닙니다 Thread. 모니터 Thread가 약간 예측할 수 없습니다.

모든 Java 객체에는 모니터가 있지만 일반적으로 전용 잠금 장치를 사용하는 것이 좋습니다.

private final Object lock = new Object();

명명 된 클래스를 사용하면 적은 메모리 비용 (프로세스 당 약 2K)으로 진단을보다 쉽게 ​​읽을 수 있습니다.

private static final class Lock { }
private final Object lock = new Lock();

하기 위해 wait또는 notify/ notifyAll객체, 당신은 함께 잠금 유지 될 필요가 synchronized문을. 또한 while깨우기 상태를 확인하기 위해 루프 가 필요합니다 (이유를 설명하기 위해 스레딩에 대한 좋은 텍스트를 찾으십시오).

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

알리기 위해:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

멀티 스레딩에 들어갈 때 Java 언어와 java.util.concurrent.locks잠금 (및 java.util.concurrent.atomic)을 모두 이해하는 것이 좋습니다. 그러나 가능하면 java.util.concurrent데이터 구조를 사용하십시오 .


5
대기와 알림이 모두 동일한 객체 (동기화)의 동기화 된 블록에 있다는 것을 감안할 때 이것이 어떻게 작동하는지 이해하지 못했습니다. 대기 스레드가 블록에 있기 때문에 통지 스레드 블록을 "동기화 (잠금)"행으로 설정해서는 안됩니까?
Brent212

6
@ Brent212 이외의 다른 방법의 경우 wait, 절대 접근 할 수 없습니다 notify. 그러나에 대한 API 문서 Object.wait에서 "스레드가이 모니터의 소유권을 해제합니다". 따라서 wait마치 마치 둘러싸는 synchronized블록 외부에있는 것처럼 보입니다 (동일한 객체의 경우 같은 객체에 여러 synchronized블록이 있을 수 있음 ).
Tom Hawtin-tackline

24

나는이 스레드가 거의 2 세라는 것을 알고 있지만 동일한 문제 로이 Q / A 세션에 왔기 때문에 여전히 이것을 닫아야합니다 ...

이 illegalMonitorException 정의를 반복해서 읽으십시오.

IllegalMonitorException은 스레드가 객체의 모니터를 기다리려고 시도했거나 지정된 모니터를 소유하지 않고 객체의 모니터를 기다리는 다른 스레드에 알리려고 시도했음을 나타냅니다.

이 줄은 두 가지 상황 중 하나가 발생할 때 IllegalMonitorException이 계속 발생한다고 말합니다.

1> 지정된 모니터를 소유하지 않고 객체의 모니터를 기다립니다.

2> 지정된 모니터를 소유하지 않고 객체의 모니터를 기다리는 다른 스레드에 알립니다.

어떤 사람들은 대답을 얻었을 수도 있습니다 ... 모두 그렇지 않은 사람은 2 명을 확인하십시오 ...

동기화 (개체)

object.wait ()

물체 모두 가 동일한 에는 illegalMonitorException이 발생하지 않습니다.

이제 IllegalMonitorException 정의를 다시 읽으면 다시 잊어 버리지 않습니다.


실제로, 그것은 작동하지 않습니다. 나는 그것을 시도했다. Runnable을 만들고 (동기화 된 블록을 사용하여) 잠그고 그 블록 내부에서 UI 스레드 (Android)에서 Runnable을 실행 한 후 myRunnable.wait ()를 수행하지만 여전히 예외가 발생합니다.
Ted

Excelente 설명 !! 객체를 지정하지 않고 wait ()를 수행했기 때문에 인스턴스를 가져 와서 다른 객체와 동기화했습니다. 이제 otherObject.wait ()을 사용하고 있으며 작동합니다!
Fersca

6

귀하의 의견을 바탕으로 다음과 같은 일을하는 것처럼 들립니다.

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

세 가지 문제가 있습니다.

  1. 다른 사람들이 말했듯 obj.wait()이 현재 스레드가 기본 잠금 / 뮤텍스를 보유하고있는 경우에만 호출 할 수 있습니다 obj. 현재 스레드가 잠금을 보유하지 않으면 현재보고있는 예외가 발생합니다.

  2. thread.wait()전화는 당신이 그것을 할 것으로 예상 것으로 보인다 무엇을하지 않습니다. 특히 지정된 스레드가 대기 thread.wait() 하지 않습니다 . 오히려 원인 현재 스레드가 다른 스레드를 호출 할 때까지 기다려야 thread.notify()또는 thread.notifyAll().

    Thread원하지 않는 경우 인스턴스를 강제 로 일시 중지 시키는 안전한 방법은 실제로 없습니다 . (Java가 가장 가까운 Thread.suspend()방법 은 더 이상 사용되지 않는 방법이지만 Javadoc에 설명 된대로 해당 방법은 본질적으로 안전하지 않습니다 .)

    새로 시작 Thread을 일시 중지하려면 가장 좋은 방법은 CountdownLatch인스턴스 를 만들고 await()래치 에서 스레드를 호출 하여 일시 중지하는 것입니다. 그러면 메인 스레드는 countDown()래치를 호출 하여 일시 중지 된 스레드가 계속되도록합니다.

  3. 이전 포인트와 직교하여 Thread객체를 잠금 / 뮤텍스로 사용 하면 문제가 발생할 수 있습니다. 예를 들어, javadoc Thread::join은 다음 과 같이 말합니다.

    이 구현은에 설정된 루프 this.wait호출을 사용합니다 this.isAlive. 스레드가 종료되면 this.notifyAll메소드가 호출됩니다. 이 응용 프로그램을 사용하지 않는 것이 좋습니다 wait, notify또는 notifyAllThread인스턴스.


2

코드를 게시하지 않았으므로 우리는 어둠 속에서 일하고 있습니다. 예외의 세부 사항은 무엇입니까?

스레드 내부 또는 외부에서 Thread.wait ()을 호출하고 있습니까?

IllegalMonitorStateException에 대한 javadoc에 따르면 다음과 같습니다.

스레드가 지정된 모니터를 소유하지 않고 오브젝트의 모니터에서 대기하려고 시도했거나 오브젝트의 모니터에서 대기중인 다른 스레드에게 알리려고 시도했음을 표시합니다.

이 답변을 명확히하기 위해 스레드를 기다리는이 호출은 동기화 된 블록 내에서 호출되었지만 IllegalMonitorStateException도 발생합니다.


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }

@ CPerkins : 실행 스레드와 대상 객체를 혼동하고 있다고 생각합니다 wait().
Robert Munteanu

@Robert-아마도 나는 생각하지 않습니다. Thread 인스턴스를 시작한 다음 기다릴 것을 요청하면 설명하려는 IllegalMonitorStateException이 발생합니다.
CPerkins

worker.wait()회선 에 대해 이야기하고 있습니까? 그런 다음 잠금 장치가 아닌 작업자와 동기화해야합니다.
Robert Munteanu

1

IllegalMonitorStateException을 처리하려면 호출 스레드가 적절한 모니터를 소유 한 경우에만 wait, notify 및 notifyAll 메소드의 모든 호출이 발생하는지 확인해야합니다 . 가장 간단한 해결책은 이러한 호출을 동기화 된 블록 안에 넣는 것입니다. 동기화 된 명령문에서 호출 될 동기화 오브젝트는 모니터를 가져와야합니다.

다음은 모니터 개념을 이해하기위한 간단한 예입니다.

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

0

Thread.wait () 호출은 Thread.class 객체에서 동기화되는 코드 내에서 의미가 있습니다. 나는 그것이 당신이 의미하는 것이라고 생각하지 않습니다.
물어

스레드가 알림을받을 때까지 기다리려면 어떻게해야합니까?

현재 스레드 만 기다릴 수 있습니다. 다른 스레드는 동의하는 경우에만 부드럽게 기다리도록 요청할 수 있습니다.
어떤 조건을 기다리려면 잠금 객체가 필요합니다-Thread.class 객체는 매우 나쁜 선택입니다-단일 AFAIK이므로 동기화 (스레드 정적 메소드 제외)는 위험합니다.
Tom Hawtin은 동기화 및 대기에 대한 자세한 내용을 이미 설명했습니다. java.lang.IllegalMonitorStateException동기화되지 않은 객체를 기다리려고한다는 것은 불법입니다.


0

이것이 다른 누군가에게 도움이되는지 아닌지 확실하지 않지만 이것은 사용자 "Tom Hawtin-tacklin"의 대답에서 위의 문제를 해결하는 핵심 부분이었습니다.

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

"lock"이 synchronized ()에서 인수로 전달되고 "lock"에서도 사용된다는 사실 .notifyAll ();

이 두 곳에서 만들면 제대로 작동합니다.


0

IllegalMonitorStateException다른 class/ 스레드 에서 /에서 스레드를 깨우려고 하는 동안 잠시 받았습니다 . 에서 java 8당신에게 사용할 수있는 lock새로운 동시성 API의 기능을 대신synchronized기능을.

이미 asynchronous웹 소켓 트랜잭션을 위한 객체를에 저장 했습니다 WeakHashMap. 필자의 경우 해결책은 회신을 위해 객체를 저장하는lockConcurrentHashMap 것이 었습니다 synchronous. (not 아님 )에 유의 하십시오 .condition.await.wait

멀티 스레딩을 처리하기 위해 a Executors.newCachedThreadPool()를 사용하여 스레드 풀 을 만들었습니다 .


0

Java 7.0 이하 버전을 사용하는 사람들은 여기에서 사용한 코드를 참조 할 수 있으며 작동합니다.

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.