자바에서 실제로 스플리어스 웨이크업이 발생합니까?
잠금과 관련된 다양한 질문과 (거의) 항상 '스플리어스 웨이크업으로 인한 루프'라는1 용어를 발견하는 것을 보면, 이런 종류의 웨이크업(예를 들어 적절한 하드웨어/소프트웨어 환경을 가정한 경우)을 경험한 사람이 있습니까?
'궁금하다'는 말은 명백한 이유가 없다는 것을 알지만, 그런 종류의 사건에 대한 이유는 무엇일까?
(1주의: 루프 연습에 의문을 제기하는 것은 아닙니다.)
편집: 도움말 질문(코드 샘플을 좋아하는 사용자용):
다음 프로그램을 실행하고 있는 경우:
public class Spurious {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try {
try {
cond.await();
System.out.println("Spurious wakeup!");
} catch (InterruptedException ex) {
System.out.println("Just a regular interrupt.");
}
} finally {
lock.unlock();
}
}
}
이걸 깨우려면 어떻게 해야 하죠?await
무작위로 이벤트를 기다릴 필요 없이 충동적으로 행동할 수 있을까요?
가짜 깨우기에 관한 위키피디아 기사에는 다음과 같은 내용이 있습니다.
그
pthread_cond_wait()
Linux 의 함수는, 를 사용해 실장됩니다.futex
시스템 콜을 실행합니다.Linux에서의 각 블로킹시스템 콜은 다음 명령과 함께 갑자기 반환됩니다.EINTR
프로세스가 신호를 수신할 때...pthread_cond_wait()
대기 시간을 재기동할 수 없는 것은, 대기 시간이 얼마 지나지 않아 실제의 깨우기를 놓칠 가능성이 있기 때문입니다.futex
시스템 콜을 실행합니다.이 레이스 조건은, 불변수를 체크하는 발신자만이 회피할 수 있습니다.따라서 POSIX 신호는 스플리어스 웨이크업을 생성합니다.
개요: Linux 프로세스가 시그널링되면 대기 스레드 각각이 핫 스플리어스 웨이크업 상태가 됩니다.
제가 살게요.그것은 흔히 말하는 "실적을 위해서"라는 모호한 이유보다 삼키기 쉬운 약이다.
저는 이 동작을 나타내는 생산 시스템을 가지고 있습니다.스레드는 큐에 메시지가 있음을 나타내는 신호를 대기합니다.사용량이 많은 시간에는 최대 20%의 웨이크업이 가짜입니다(웨이크업 시 큐에 아무것도 없습니다).이 스레드는 메시지의 유일한 사용자입니다.Linux SLES-10 8 프로세서박스로 동작하며 GCC 4.1.2로 구축되어 있습니다.외부 소스로부터의 메시지이며, 시스템이 메시지를 충분히 빨리 읽지 않으면 문제가 발생하기 때문에 비동기적으로 처리됩니다.
제목에 있는 질문에 대답하는 것 - 네! 실제로 일어나는 일입니다.Wiki 기사에서는 가짜 깨우기에 대해 많은 것을 언급하고 있지만, 내가 우연히 알게 된 것과 같은 좋은 설명은 다음과 같다.
생각해 봐...다른 코드와 마찬가지로 스레드스케줄러는 기반이 되는 하드웨어/소프트웨어에서 비정상적인 현상이 발생하여 일시적으로 정전이 발생할 수 있습니다.물론 가능한 한 드물게 이러한 현상이 발생할 수 있도록 주의해야 하지만 100% 강력한 소프트웨어는 없기 때문에 스케줄러가 이를 감지한 경우(예를 들어 하트비트 누락 관찰)에 대비하여 정상적인 복구에 주의를 기울이는 것이 타당합니다.
그러면 스케줄러는 어떻게 회복할 수 있을까요?블랙아웃 중에는 대기 스레드에 통지하기 위한 신호를 놓칠 수 있습니다.스케줄러가 아무것도 하지 않으면 언급된 "불행한" 스레드가 중단되어 영원히 대기하게 됩니다.이것을 피하기 위해서, 스케줄러는 대기하고 있는 모든 스레드에 신호를 송신하기만 하면 됩니다.
따라서 대기 스레드에 이유 없이 통지할 수 있는 "계약"을 확립해야 합니다.정확히는 스케줄러 블랙아웃이라는 이유가 있지만 스레드는 스케줄러 내부 구현 세부사항을 인식하지 않도록 설계되어 있기 때문에 이 이유는 "스플리어스"로 표시되는 것이 좋습니다.
Source에서 이 답변을 읽었는데 충분히 타당하다는 것을 알았습니다.또, 읽기
PS: 위 링크는 스플리어스 웨이크업에 대한 자세한 내용을 담고 있는 개인 블로그입니다.
Cameron Purdy는 얼마 전에 가짜 깨우기 문제에 대해 블로그에 글을 썼다.그래, 그럴 수 있어
자바가 도입되는 플랫폼 중 일부에 제한이 있기 때문에 (가능성이 있는) 사양에 있다고 생각합니다만, 제가 틀렸을지도 모릅니다.
덧붙이자면.네, 24 코어 머신(JDK 6)에서 멀티 스레딩 문제의 원인을 3일간 조사했습니다.실행 10건 중 4건은 패턴 없이 실행되었습니다.2코어 또는 8코어에서는 이 문제가 발생하지 않았습니다.
일부 온라인 자료를 조사했는데, 이는 자바 문제가 아니라 일반적인 드물지만 예상된 동작입니다.
OP의 질문에 대한 답변
어떻게 하면 무작위로 일어나는 이벤트를 영원히 기다리지 않고 이 대기 상태를 활기차게 깨울 수 있을까요?
, 어떤 스플리어스 웨이크업도 이 대기 스레드를 깨울 수 없습니다.
특정 플랫폼에서 스플리어스 웨이크업이 발생할 수 있는지 여부에 관계없이 OP의 스니펫의 경우, 이는 완전히 불가능합니다.Condition.await()
를 클릭하여 출력 스트림에 "스플리어스 웨이크업!" 행을 표시합니다.
매우 이국적인 Java Class Library를 사용하지 않는 한
이는 표준 OpenJDK의ReentrantLock
의 메서드newCondition()
를 반환하다AbstractQueuedSynchronizer
의 실장Condition
인터페이스, 네스트됨ConditionObject
(그건 그렇고, 이것은 의 유일한 실장입니다.Condition
이 클래스 라이브러리의 인터페이스) 및ConditionObject
의 메서드await()
는, 그 상태가 유지되고 있지 않은지 아닌지를 체크해, 어떠한 스플리어스 웨이크업도, 이 메서드를 잘못해 되돌리는 일은 없습니다.
그런데, 한번 가짜 깨우기를 에뮬레이트하는 것은 꽤 쉬우므로 직접 확인할 수 있습니다.AbstractQueuedSynchronizer
- 기반 구현이 포함됩니다. AbstractQueuedSynchronizer
로우 레벨 사용LockSupport
의park
그리고.unpark
메서드를 호출하는 경우LockSupport.unpark
기다리고 있는 실타래 위에서Condition
이 액션은 스플리어스 웨이크업과 구별할 수 없습니다.
OP의 스니펫을 약간 리팩터링하면
public class Spurious {
private static class AwaitingThread extends Thread {
@Override
public void run() {
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try {
try {
cond.await();
System.out.println("Spurious wakeup!");
} catch (InterruptedException ex) {
System.out.println("Just a regular interrupt.");
}
} finally {
lock.unlock();
}
}
}
private static final int AMOUNT_OF_SPURIOUS_WAKEUPS = 10;
public static void main(String[] args) throws InterruptedException {
Thread awaitingThread = new AwaitingThread();
awaitingThread.start();
Thread.sleep(10000);
for(int i =0 ; i < AMOUNT_OF_SPURIOUS_WAKEUPS; i++)
LockSupport.unpark(awaitingThread);
Thread.sleep(10000);
if (awaitingThread.isAlive())
System.out.println("Even after " + AMOUNT_OF_SPURIOUS_WAKEUPS + " \"spurious wakeups\" the Condition is stil awaiting");
else
System.out.println("You are using very unusual implementation of java.util.concurrent.locks.Condition");
}
}
파킹 해제(메인) 스레드가 대기 스레드를 깨우려고 아무리 애를 써도Condition.await()
이 경우 메서드는 반환되지 않습니다.
의 가짜 웨이크업Condition
의 대기 메서드에 대해서는 interface의 javadoc에서 설명합니다.그렇게 써있긴 하지만
상태를 기다리는 동안 가짜 웨이크업이 발생할 수 있습니다.
그리고 그것
애플리케이션 프로그래머는 항상 발생할 수 있다고 가정하고 항상 루프에서 대기하는 것이 좋습니다.
그러나 나중에 그것이 추가된다.
구현은 스플리어스 웨이크업 가능성을 배제하기 위해 자유롭다.
그리고.AbstractQueuedSynchronizer
의 실장Condition
interface를 사용하면 스플리어스 웨이크업 가능성이 없어집니다.
이것은 다른 사람에게도 해당된다.ConditionObject
는 메서드를 대기하고 있습니다.
결론은 다음과 같습니다.
우리는 항상 전화해야 한다.Condition.await
루프에서 조건이 유지되지 않는지 확인합니다.단, 표준 OpenJDK에서는 Java Class Library는 발생하지 않습니다.다시 말하지만 매우 특이한 Java Class Library를 사용하지 않는 한(이것은 매우 특이한 것이어야 한다.왜냐하면 OpenJDK 이외의 잘 알려진 Java Class Libraries, 현재 거의 멸종된 GNU Classpath 및 Apache Harmony가 표준 구현과 동일한 것으로 보이기 때문이다).Condition
★★★★★★★★★★★★★★★★★★)
https://stackoverflow.com/a/1461956/14731에는 기본 운영체제가 웨이크업을 트리거하지 않더라도 스플리어스 웨이크업으로부터 보호할 필요가 있는 이유에 대한 훌륭한 설명이 기재되어 있습니다.이 설명은 Java를 포함한 여러 프로그래밍 언어에 적용됩니다.
언급URL : https://stackoverflow.com/questions/1050592/do-spurious-wakeups-in-java-actually-happen
'programing' 카테고리의 다른 글
자바에서는 어떤 컨스트럭터를 다른 컨스트럭터에서 어떻게 호출합니까? (0) | 2022.07.19 |
---|---|
두 번째 레벨 vue 경로에 직접 액세스할 때 오류가 발생함 (0) | 2022.07.19 |
왼쪽에 0이 있는 정수를 채우려면 어떻게 해야 하나요? (0) | 2022.07.19 |
Vuejs 2개의 어레이가 변경에 반응하지 않도록 어레이를 다른 어레이에 할당하는 방법 (0) | 2022.07.19 |
Java 코드 몇 줄의 문자열에 대한 URL 읽기 (0) | 2022.07.19 |