2010-04-12 6 views
7

나는 다음과 같은 프로그램이 있습니다이해 자바 대기 및 통지 방법

import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 


public class SimpleWaitNotify implements Runnable { 

final static Object obj = new Object(); 
static boolean value = true; 

public synchronized void flag() { 
    System.out.println("Before Wait"); 
    try { 
     obj.wait(); 
    } catch (InterruptedException e) { 
     System.out.println("Thread interrupted"); 
    } 
    System.out.println("After Being Notified"); 
} 

public synchronized void unflag() { 
    System.out.println("Before Notify All"); 
    obj.notifyAll(); 
    System.out.println("After Notify All Method Call"); 
} 

public void run() { 
    if (value) { 
     flag(); 
    } else { 
     unflag(); 
    } 
} 

public static void main(String[] args) throws InterruptedException { 
    ExecutorService pool = Executors.newFixedThreadPool(4); 
    SimpleWaitNotify sWait = new SimpleWaitNotify(); 
    pool.execute(sWait); 
    SimpleWaitNotify.value = false; 
    SimpleWaitNotify sNotify = new SimpleWaitNotify(); 
    pool.execute(sNotify); 
    pool.shutdown(); 

} 

}

내가 OBJ에서 대기, 나는 두 개의 스레드 각각에 대해 다음과 같은 예외 Exception in thread "pool-1-thread-1" java.lang.IllegalMonitorStateException: current thread not owner를 얻을 수 있습니다.

그러나 SimpleWaitNotify의 모니터를 사용하면 프로그램 실행이 일시 중단됩니다. 즉, 현재 실행 스레드 및 실행 프로그램을 일시 중단한다고 생각합니다. 진행 상황에 대한 이해를 돕기 위해 도움을받을 수 있습니다.

이것은 이론과 javadoc이 단순한 것처럼 보이는 영역이며 많은 예제가 없기 때문에 개념적으로 나에게 큰 차이가 남았습니다.

답변

12

당신은 objwaitnotifyAll를 호출하고 있지만 (당신이 동기화 방법을 가지고 있기 때문에) 당신은 this에 동기화하고 있습니다.

기다리거나 알리려면 먼저 모니터를 "소유"해야합니다. 방법을 Unsynchronize, 대신 OBJ에 동기화 :

public void flag() { 
    System.out.println("Before Wait"); 
    synchronized (obj) { 
     try { 
      obj.wait(); 
     } catch (InterruptedException e) { 
      System.out.println("Thread interrupted"); 
     } 
    } 
    System.out.println("After Being Notified"); 
} 

public void unflag() { 
    System.out.println("Before Notify All"); 
    synchronized (obj) { 
     obj.notifyAll(); 
    } 
    System.out.println("After Notify All Method Call"); 
} 
+0

좋아요. 복합 객체'obj'를 완전히 없애 버리면 같은 결과를 얻을 수 있습니까? – Maddy

+4

@Maddy : "this"를 기다리고 기다릴 수 있어야합니다. 일반적으로 "this"는 다른 코드가 효과적으로 액세스 할 수있는 참조이므로 일반적으로 나쁜 생각입니다. 코드 만 알고있는 객체에 대한 개인 참조를 유지하는 것이 좋습니다. –

+0

좋은 지적. 동의했다. – Maddy

3

어느 synchronizeobj에, 또는 thiswaitnotify를 호출합니다. 호출하는 스레드는 같은 모니터 개체를 호출해야합니다. 예를 들어

이 예에서는

synchronized void flag() { 
    System.out.println("Before Wait"); 
    try { 
    wait(); 
    } catch (InterruptedException e) { 
    System.out.println("Thread interrupted"); 
    } 
    System.out.println("After Being Notified"); 
} 

가 잠금이 this에 유지된다 (수정 synchronized는 인스턴스 방법에 사용되는 경우, 인스턴스의 모니터를 취득). 따라서 wait() 메서드는 암시 된 인스턴스 this에서 호출 될 수 있습니다. 두 개의 스레드를 조정하기 위해


, 그들은 같은 잠금을 공유 할 필요가있다. 원래 버전은 잠금으로 사용할 수있는 정적 인 obj을 가졌지 만 synchronized 블록에서는 사용되지 않았습니다. 여기에 더 좋은 예입니다 직접 문 pool.shutdown() 퍼팅

class SimpleWaitNotify implements Runnable { 

    private final Object lock; 
    private final boolean wait; 

    SimpleWaitNotify(Object lock, boolean wait) { 
    this.lock = lock; 
    this.wait = wait; 
    } 

    public void flag() { 
    synchronized (lock) { 
     System.out.println("Before Wait"); 
     try { 
     lock.wait(); 
     System.out.println("After Being Notified"); 
     } catch (InterruptedException ex) { 
     System.out.println("Thread interrupted"); 
     } 
    } 
    } 

    public void unflag() { 
    synchronized(lock) { 
     System.out.println("Before Notify All"); 
     lock.notifyAll(); 
     System.out.println("After Notify All Method Call"); 
    } 
    } 

    public void run() { 
    if (wait) { 
     flag(); 
    } else { 
     unflag(); 
    } 
    } 

    public static void main(String[] argv) throws Exception { 
    ExecutorService pool = Executors.newFixedThreadPool(4); 
    Object shared = new Object(); 
    SimpleWaitNotify sWait = new SimpleWaitNotify(shared, true); 
    pool.execute(sWait); 
    SimpleWaitNotify sNotify = new SimpleWaitNotify(shared, false); 
    pool.execute(sNotify); 
    pool.shutdown(); 
    } 

} 
+0

대신 SimpleWait 객체 자체에서 대기하도록 변경했습니다. 그러나이 경우 프로그램이 중단되지 않습니다. 오히려 일시 중단 된 스레드는 알리지 않습니다. 다음과 같이 인쇄됩니다. 대기 전 대기 모두에게 알리기 전에 모든 방법으로 알린 후 – Maddy

+0

@ Maddy 알겠습니다. 문제가 발생합니다. 내 업데이트를 확인하십시오. – erickson

1

오히려 다음과 같이하십시오.

while (!service.isTerminated()) 
{ 
    service.shutdown(); 
} 

그래서 모든 스레드 실행이 완료 될 때까지 기다립니다.