이 장에서는 스레드 대기/웨이크 업 메소드를 소개합니다. 관련된 내용에는 다음이 포함됩니다.
1. WAKE (), notify (), notifyall () 및 기타 메소드 소개
2. WAIT () 및 NOTIFY ()
3. 기다려 (긴 시간) 및 notify ()
4. WAIT () 및 notifyAll ()
5. Thread가 아닌 객체에 정의 된 Notify (), Wait () 및 기타 함수가있는 이유
Wait (), notify (), notifyall () 및 기타 메소드에 대한 소개
Object.java에서 Wait (), notify () 및 notifyall ()과 같은 인터페이스가 정의됩니다. Wait ()의 함수는 현재 스레드가 대기 상태로 들어가도록하는 것입니다. Wait ()도 현재 스레드가 보유한 잠금 장치를 해제하도록합니다. notify () 및 notifyall ()의 역할은 현재 객체에서 대기 스레드를 깨우는 것입니다.
객체 클래스에서 대기/깨우기에 대한 API 세부 사항은 다음과 같습니다.
notify () -이 객체 모니터에서 대기 대기하는 단일 스레드를 깨우십시오.
notifyall () -이 객체 모니터에서 대기하는 모든 스레드를 깨우십시오.
대기 () - 현재 스레드를 "대기 (차단) 상태"에 넣고 "다른 스레드 가이 객체의 notify () 메소드 또는 notifyall () 메소드를 호출 할 때까지", 현재 스레드가 깨어납니다 (입력하십시오. " 준비 상태 ").
대기 (Long Timeout) - 현재 스레드가 "대기 (차단) 상태"에, "다른 스레드 가이 개체의 notify () 메소드 또는 notifyall () 메소드를 호출하거나 지정된 시간을 초과 할 때까지", ", 지정된 시간을 초과 할 때까지", 그리고 현재 실이 깨어났다 ( "준비"를 입력).
대기 (Long Timeout, int nanos) - 다른 스레드가 다른 스레드가 Notify () 메소드 또는 notifyall () 메소드를 호출 할 때까지 현재 스레드를 "대기 (차단) 상태에 넣거나 다른 스레드가 현재 스레드를 인터럽트합니다. 또는 실제 시간이 초과되었고 현재 실이 깨어났다 ( "준비 상태"에 입력).
2. WAIT () 및 notify () 예제
다음은 "Wait () 및 notify () 함께 ()를 보여주는 예입니다.
코드 사본은 다음과 같습니다.
// WaitTest.java 소스 코드
클래스 스레드는 스레드를 확장합니다 {
public threada (문자열 이름) {
슈퍼 (이름);
}
public void run () {
동기화 (this) {
System.out.println (thread.currentThread (). getName ()+"Call notify ()");
// 현재 대기 스레드를 깨우십시오
notify ();
}
}
}
공개 클래스 대기 테스트 {
public static void main (String [] args) {
Threada t1 = 새로운 Threada ( "T1");
동기화 (t1) {
노력하다 {
// "스레드 t1"시작
system.out.println (thread.currentthread (). getName ()+"시작 t1");
t1.start ();
// 메인 스레드는 t1이 notify ()를 통해 깨어날 때까지 기다립니다.
system.out.println (thread.currentthread (). getName ()+"Wait ()");
T1.WAIT ();
system.out.println (thread.currentthread (). getName ()+"계속");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
메인 시작 T1
메인 대기 ()
T1 Call Notify ()
메인 계속
결과 설명 :
다음 그림은 "메인 스레드"및 "스레드 T1"의 흐름을 보여줍니다.
(01) 그림의 "메인 스레드"는 "메인 스레드 메인"을 나타냅니다. "스레드 T1"은 대기 테스트에서 시작된 "스레드 T1"을 나타냅니다. "잠금"은 "객체 T1의 동기 잠금"을 나타냅니다.
(02) "Main Thread"는 New Threada ( "T1")를 통해 새로운 "스레드 T1"을 만듭니다. 그런 다음 "T1 객체의 동기 잠금"은 동기화 된 (T1)를 통해 얻습니다. 그런 다음 t1.start ()를 호출하여 "스레드 t1"을 시작하십시오.
(03) "메인 스레드"는 t1.wait ()를 실행하여 "t1 객체의 잠금"을 해제하고 "대기 (차단) 상태"로 들어갑니다. t1 객체의 스레드가 Notify () 또는 notifyall ()을 통해 깨어날 때까지 기다립니다.
(04) "스레드 T1"이 실행 된 후, "현재 객체의 잠금"은 동기화 된 (this)를 통해 얻습니다. "메인 스레드".
(05) "스레드 T1"이 완료된 후 "현재 객체의 잠금"을 해제하십시오. 그 후 즉시 "메인 스레드"는 "T1 객체의 잠금"을 획득 한 다음 실행됩니다.
위 코드에 대해? 친구가 한 번 물었습니다.
이 질문에 대답하기 전에 JDK 문서의 대기에 관한 단락을 살펴 보겠습니다.
코드 사본은 다음과 같습니다.
다른 스레드가 다른 스레드 가이 개체의 notify () 메소드 또는 notifyall () 메소드를 호출 할 때까지 대기 스레드가 대기합니다.
다시 말해,이 메소드는 단순히 통화 대기 (0)를 수행하는 것처럼 정확하게 동작합니다.
현재 스레드는이 객체의 모니터를 소유해야합니다. 스레드는이 모니터의 소유권을 릴리스하고 다른 스레드 가이 개체의 모니터를 알리기 위해 알림 또는 Notifyall 메소드를 기다립니다 모니터의 소유권을 재구성하고 실행을 재개 할 수 있습니다.
중국어의 의미는 대략입니다.
"현재 스레드"가 다른 스레드가 호출 될 때까지 대기하여 () 또는 notifyall ()이 스레드를 깨우기 위해 대기 할 때까지 대기합니다. 다시 말해,이 방법은 대기 (0)과 동일한 효과를 갖습니다! (대기 (Long Millis) 방법의 경우, Millis가 0 인 경우, Notify () 또는 notifyall ()에 의해 깨어날 때까지 무한 대기를 의미합니다.
"현재 스레드"가 호출 대기 ()을 호출하면 객체에 대한 동기화 잠금이 있어야합니다. 스레드 호출 대기 () 후에는 잠금이 해제됩니다. "다른 스레드"가 객체의 동기식 잠금 장치 () 또는 notifyall () 메소드를 호출 할 때까지 기다립니다. 그런 다음 스레드는 "이 객체의 동기화 잠금"을 다시 볼 때까지 계속 기다린 다음 계속 실행할 수 있습니다.
참고 : JDK의 설명에서 Wait ()의 함수는 "현재 스레드"를 기다리는 것이며 "현재 스레드"는 CPU에서 실행되는 스레드를 나타냅니다!
이것은 또한 t1.wait ()가 "스레드 t1"을 통해 호출되는 대기 () 메소드이지만 t1.wait ()가 "메인 스레드 메인"에있는 곳이라는 것을 의미합니다. 기본 스레드는 "현재 스레드", 즉 실행 상태 인 T1.wait ()가 실행되기 전에해야합니다. 따라서 현재 "현재 스레드"는 "메인 스레드 메인"입니다! 따라서 t1.wait ()는 "스레드 t1이 아닌"메인 스레드 "를 대기해야합니다!
3. 기다려 (긴 시간) 및 notify ()
대기 (Long Timeout)은 현재 스레드를 "대기 (차단) 상태"에 넣고 "다른 스레드 가이 개체의 notify () 메소드 또는 notifyall () 메소드를 호출하거나 지정된 시간을 초과 할 때까지"및 현재 스레드가 깨어났다 (입력) "준비").
다음 예제는 대기 (긴 타임 아웃) 시간 초과를 보여주고 스레드가 깨어납니다.
코드 사본은 다음과 같습니다.
// waittimeouttest.java의 소스 코드
클래스 스레드는 스레드를 확장합니다 {
public threada (문자열 이름) {
슈퍼 (이름);
}
public void run () {
System.out.println (thread.currentthread (). getName () + "run");
// 지속적으로 실행되는 악순환.
while (true)
}
}
공개 클래스 웨이 타임 아웃 테스트 {
public static void main (String [] args) {
Threada t1 = 새로운 Threada ( "T1");
동기화 (t1) {
노력하다 {
// "스레드 t1"시작
system.out.println (thread.currentthread (). getName () + "시작 t1");
t1.start ();
// 메인 스레드는 T1이 Notify () 또는 notifyall ()을 통해 깨어나거나 3000ms를 초과합니다.
system.out.println (thread.currentthread (). getName () + "호출 대기");
T1. Wait (3000);
system.out.println (thread.currentthread (). getName () + "계속");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
메인 시작 T1
메인 콜 대기
T1 실행 // 약 3 초 후 ... 출력 "메인 계속"
메인 계속
결과 설명 :
다음 그림은 "메인 스레드"및 "스레드 T1"의 흐름을 보여줍니다.
(01) 그림의 "메인 스레드"는 WaittimeoutTest 메인 스레드 (예 : 스레드 메인)를 나타냅니다. "스레드 T1"은 대기 테스트에서 시작된 스레드 T1을 나타냅니다. "잠금"은 "객체 T1의 동기 잠금"을 나타냅니다.
(02) 메인 스레드 메인은 "스레드 t1"을 시작하기 위해 t1.start ()를 실행합니다.
(03) 메인 스레드 메인은 T1.WAIT (3000)를 실행하며 현재 주 스레드는 "차단 상태"로 들어갑니다. "T1 객체 잠금에 사용 된 스레드는 Notify () 또는 notifyall ()"또는 "3000ms 타임 아웃 후"를 통해 깨우기 위해 "메인 스레드 메인이"Ready State "로 들어가면 실행할 수 있습니다.
(04) "스레드 T1"이 실행 된 후에는 죽은 루프로 들어가 계속 실행됩니다.
(05) 타임 아웃이 3000ms이면 메인 스레드 메인이 "준비 상태"로 들어간 다음 "실행 상태"로 들어갑니다.
4. WAIT () 및 notifyAll ()
이전 예제를 통해 Notify () 가이 객체 모니터에서 대기 대기하는 단일 스레드를 깨울 수 있음을 알고 있습니다.
아래에서, 우리는 예제를 통해 notifyall ()의 사용법을 보여줍니다.
코드 사본은 다음과 같습니다.
공개 클래스 NotifyAllTest {
개인 정적 객체 obj = new Object ();
public static void main (String [] args) {
Threada t1 = 새로운 Threada ( "T1");
Threada t2 = 새로운 Threada ( "T2");
Threada t3 = 새로운 Threada ( "T3");
t1.start ();
t2.start ();
t3.start ();
노력하다 {
System.out.println (Thread.currentThread (). getName ()+"sleep (3000)");
Thread.sleep (3000);
} catch (InterruptedException e) {
e.printstacktrace ();
}
동기화 (OBJ) {
// 메인 스레드가 깨우기를 기다리고 있습니다.
System.out.println (thread.currentthread (). getName ()+"notifyall ()");
obj.notifyall ();
}
}
정적 클래스 스레드는 스레드를 확장합니다.
public threada (문자열 이름) {
슈퍼 (이름);
}
public void run () {
동기화 (OBJ) {
노력하다 {
// 출력 결과를 인쇄합니다
system.out.println (thread.currentthread (). getName () + "대기");
// 현재 대기 스레드를 깨우십시오
obj.wait ();
// 출력 결과를 인쇄합니다
system.out.println (thread.currentthread (). getName () + "계속");
} catch (InterruptedException e) {
e.printstacktrace ();
}
}
}
}
}
실행 결과 :
코드 사본은 다음과 같습니다.
T1 잠깐
주요 수면 (3000)
T3 잠깐
T2 기다려
Main NotifyAll ()
T2 계속
T3 계속
T1 계속
결과 설명 :
아래 흐름도를 참조하십시오.
(01) 3 스레드 "T1", "T2"및 "T3"가 작성되어 기본 스레드에서 시작되었습니다.
(02) 메인 스레드는 수면을 통해 3 초 동안 잠을 자고 있습니다 (3000). 메인 스레드가 3 초 동안 수면 중에, 우리는 3 개의 스레드 "T1", "T2"및 "T3"이 모두 실행되고 있다고 가정합니다. "T1"을 예로 들어 보면 OBJ.Wait ()가 다른 스레드가 Notify () 또는 NofityAll ()을 통해 깨어날 때까지 실행합니다. 또한 다른 스레드가 nofity () 또는 nofityall ()을 통해 깨어날 때까지 기다립니다.
(03) 메인 스레드는 3 초 동안 자고 실행됩니다. obj.notifyall ()을 실행하여 OBJ의 대기 실을 깨우십시오. 기본 스레드의 동기화 (OBJ)가 실행 된 직후 기본 스레드는 "OBJ 잠금"을 방출합니다. 이러한 방식으로 "T1", "T2"및 "T3"은 "OBJ Lock"을 얻고 계속 실행할 수 있습니다!
5. Thread가 아닌 객체에 정의 된 Notify (), Wait () 및 기타 함수가있는 이유
wait (), notify ()와 같은 함수는 동기화 된 것과 같이 "객체 동기화 잠금"에서 작동합니다.
대기 ()는 "현재 스레드"가 대기 상태로 들어가기 때문에 스레드는 잠금 상태에서 "동기 잠금"을 해제해야합니다. 그렇지 않으면 다른 스레드는 "동기 잠금"을 얻지 못합니다. 실행할 수 없습니다!
자, 스레드가 Wait () 호출 후에는 잠금 상태에서 보유한 "동기 잠금"을 릴리스합니다. 이제 질문에 대해 생각해보십시오. 또는 Wait ()와 notify ()의 상관 관계는 무엇입니까? 대답은 "객체 동기화 잠금"을 기반으로합니다.
대기 스레드를 깨우는 스레드 (우리는 그것을 "Wake Up Thread"라고 부릅니다) "객체의 동기화 잠금"만 획득합니다 (여기서 동기화 잠금은 대기 스레드의 동기화 잠금과 동일해야 함), 호출 notify () 또는 notifyall () 메소드 후 대기 스레드를 깨울 수 있습니다. 그러나 대기 실이 깨어 났지만, 웨이크 업 스레드는 여전히 "객체의 동기 잠금"을 유지하기 때문에 즉시 실행할 수 없습니다. "오브젝트의 동기화 잠금"을 얻고 계속 실행되기 전에 웨이크 업 스레드가 "객체의 동기화 잠금"을 풀 때까지 기다려야합니다.
요컨대, Notify (), wait ()는 객체 잠금으로 고정 된 "동기 잠금"에 의존하며 각 객체는 하나만 있습니다! 그렇기 때문에 Notify (), wait ()와 같은 함수가 스레드 클래스가 아닌 객체 클래스에 정의됩니다.