2017-04-08 6 views
2

스레드 간 통신에서 특정 스레드를 어떻게 호출 할 수 있습니까?Java의 특정 스레드에 알리는 방법

아래 프로그램에는 두 개의 스레드 t1t2이 있습니다. 내가 t1.notify()를 호출 할 때

그것은 제기

Exception in thread "Thread-1" java.lang.IllegalMonitorStateException 
    at java.lang.Object.notify(Native Method) 
    at Shared.methodTwo(NotifyThread.java:43) 
    at Thread2.run(NotifyThread.java:77) 
Error 

class Shared { 

    Thread1 t1 ; 
    Thread2 t2 ; 

    void ThreadInit(Thread1 t1 , Thread2 t2) { 
     this.t1 = t1 ; 
     this.t2 = t2 ; 
    } 

    synchronized void methodOne() 
    { 
     Thread t = Thread.currentThread(); 

     System.out.println(t.getName()+" is relasing the lock and going to wait"); 

     try 
     { 
      wait();  //releases the lock of this object and waits 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 

     System.out.println(t.getName()+" got the object lock back and can continue with it's execution"); 
    } 

    synchronized void methodTwo() 
    { 
     Thread t = Thread.currentThread(); 

     try 
     { 
      Thread.sleep(5000); 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 

     t1.notify();  

     System.out.println("A thread which is waiting for lock of this object is notified by "+t.getName()); 
    } 
    } 

    class Thread1 extends Thread 
    { 
    Shared s ; 
    Thread1(Shared s) { 

     this.s = s ; 
    } 

    public void run() 
      { 
       s.methodOne(); //t1 calling methodOne() of 's' object 
      } 

    } 

    class Thread2 extends Thread { 
     Shared s ; 
    Thread2(Shared s) { 

     this.s = s ; 

    } 

    public void run() 
      { 
       s.methodTwo(); //t1 calling methodOne() of 's' object 
      } 


    } 
    public class NotifyThread 
    { 
    public static void main(String[] args) 
    { 
     final Shared s = new Shared(); 

     Thread1 t1 = new Thread1(s) ; 
     Thread2 t2 = new Thread2(s) ; 

     s.ThreadInit(t1,t2) ; 

     t1.start(); 
     t2.start(); 
    } 
} 

답변

3

당신은 특정 스레드를 통지하지 않습니다. 잠금 개체에 대한 알림을 호출합니다. 이것에 의해, 락을 대기하고있는 thread가 기동합니다. (귀하의 경우에는, 락 객체가. 그러나, 아래를 참조하십시오. 오히려 사진을 혼란 스레드입니다 ...) 스레드 즉 통지합니다 (현재을하고 있기 때문에

그러나 문제합니다 (IllegalMonitorStateException가) 발생 스레드)가 잠금을 보유하지 않습니다. 현재 스레드가 잠금을 알릴 때 잠금을 보유해야한다는 것이 (어려운) 요구 사항입니다.

은 자세한 내용은 Object.wait(timeout) 또는이 (예를 들어)에 JavaDoc을 읽어 : 나는 잠금 개체로 Thread 객체를 사용하지 않을 http://howtodoinjava.com/core-java/multi-threading/how-to-work-with-wait-notify-and-notifyall-in-java/


. 아마도 작동 할 것이지만 런타임 시스템의 무언가가 Thread 객체를 잠 그거나 대기하거나 알리는 기회 일 수도 있습니다. 그러면 일이 매우 혼란 스러울 것입니다.

이 목적을 위해 특별히 잠금 개체를 만드는 것이 더 좋습니다. 예 :


private final Object lock = new Object(); 

또한, Thread를 확장하는 클래스를 작성하는 것은 일반적으로 좋은 생각입니다. 일반적으로 Runnable 인터페이스를 구현하고 인스턴스화 한 다음이 인스턴스를 매개 변수로 Thread 생성자에 전달하는 것이 더 좋습니다. 예 : Runnable을 구현하기보다는 Thread를 확장

Thread t = new Thread(new Runnable() { 
    public void run() { 
     System.out.println("Hello world"); 
    }}); 
t.start(); 

장점 중 하나는 당신을 위해 스레드의 수명주기를 관리하는 것을보다 쉽게 ​​코드를 사용할 수 있다는 것입니다; 예 : ExecutorService, 포크 - 조인 스레드 풀 또는 클래식 스레드 풀.

두 번째는 가벼운 스레드 로직을 익명의 클래스로 간결하게 구현할 수 있다는 것입니다. 예제에서와 같이.

+0

하지만 스레드에 알리지는 않습니다. 잠금에 대한 알림을 호출하고 통지를 os 스케줄러에 위임합니다. 그리고 스케줄러는 대기 세트의 어떤 스레드가 통지를 받을지 결정합니다. 내가 아는 한 알지만, 선택한 단어는 어떤 스레드가 알림을 받았다는 사실을 알리는 스레드가 아닌 중요한 포인트가되지 않는 것 같습니다. 어떤 OP가 혼란스러워 할지도 모릅니다. –

+0

더 좋습니까? –

+0

그리고 그 말은 (꽤 괜찮은 충고입니다) 나는 기다림/통보를 전혀 사용하지 않을 것입니다. 'java.util.concurrent' 패키지의 클래스를 사용하고 싶습니다 :'Lock','Semaphore' 및'CyclicBarrier'는 [모두 더 좋은 방법입니다] (http://winterbe.com/posts/2015/04/30)/java8-concurrency-tutorial-synchronized-locks-examples /)를 사용하는 것이 좋습니다. – markspace

1

몇 가지 점을 추가하려면,

코드에서 고유 잠금을 사용하고 있습니다. JVM의 모든 오브젝트에는 자체 잠금이 있습니다. 이 잠금은 객체의 기능과 아무 관련이 없습니다. 잠금을 단독으로 획득하면 synchronized 키워드 사용과 같은 추가 조치가 없으면 다른 스레드가 객체의 내용을 원 클릭하지 못하게합니다. 스레드에서 알림을 호출한다고해서 특정 스레드가 알림을 수신한다는 의미는 아닙니다.

이전에 Thread 객체에 대한 잠금을 얻는 것이 바람직하지 않습니다. 스레드의 조인 메소드는에 조인 된 스레드의 고유 잠금을 사용합니다.코드가 다른 이유에 의해 락을 취득하면 (자), thread는 그것이 신경 쓰지 않는 몇개의 조건을 통지받을 수가 있습니다.

내장 잠금은 대기중인 스레드를 OS 스케줄러에 알리는 중개 역할입니다. OS 스케줄러는 잠금에 대한 대기 세트의 스레드가 통지를 받도록 결정합니다. 스레드가 객체에 대한 통지를 호출하면, 스레드는 대기중인 스레드가 통지를 받을지를 스케줄러에게 알려주도록 객체에 대한 잠금을 알려줍니다. 잠금은 대기중인 스레드를 알고 있지만 대기중인 상태를 모릅니다. (ReentrantLock은 이것에 대한 커다란 개선입니다. API 문서의 Condition을 참조하십시오.)

물론 notifyAll은 대기 세트의 모든 스레드를 깨우지 만 잠금 및 스케줄러가 알고있는 것입니다. notifyAll을 호출하는 스레드는 대기중인 스레드를 알지 못합니다. 이 시스템은 스레드가 다른 스레드에 직접 알릴 수 없도록 의도적으로 설계되었습니다.

또 다른 것은 조건을 확인하지 않고 전화 대기가 신뢰할 수 없다는 것입니다. 통지가 발행되기 전에 thread가 락을 취득하지 않으면 (자), 그 통지를 잃어 버립니다. 스레드가 알림을 받으면 다음에 잠금을받을 것이라고 보장 할 수 없으며 다른 스레드가 작동하여 알림 스레드가 예상하는 상태를 무효화 할 수 있습니다. 현재 스레드가 객체의 상태가 스레드가 기대하는 것임을 확인하기 위해 검사 할 수있는 내부 상태가 있어야하며 루프에서 검사로 확인하십시오.

예를 들어 고정 크기 블록 대기열 (내부적으로 목록을 사용하여 동기화를 사용하여 동시 액세스로부터 보호) 스레드가 대기열에서 항목을 가져 오려고하지만 대기열이 비어있는 경우 차단하면 take 메소드는 대기 스레드가 깨어 잠금을 재 취득하면

public synchronized T take() throws InterruptedException { 
    while (list.isEmpty()) { 
     wait(); 
    } 
    notifyAll(); 
    return list.remove(0); 
} 

, 그것은 현재의 상황이 기다리고 된 것입니다 있는지 확인합니다 :처럼 보인다. 이 경우에만 스레드가 루프를 빠져 나가야합니다.