Java에서 가짜 웨이크 업이 실제로 발생합니까?


208

다양한 잠금 관련 질문을보고 (거의) 항상 '스퓨리어스 웨이크 때문에 루프를 찾는다'라는 용어가 궁금합니다 .1 누군가가 그런 종류의 웨이크 업을 경험 한 적이 있습니까 (예 : 괜찮은 하드웨어 / 소프트웨어 환경 가정)?

나는 '스퓨리어스 (spurious)'라는 용어가 명백한 이유를 의미하지는 않지만 그러한 종류의 사건에 대한 이유는 무엇입니까?

( 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임의의 이벤트를 영원히 기다리지 않고 이를 가짜 로 깨우려면 어떻게해야 합니까?


1
POSIX 시스템에서 실행되고 pthread_cond_wait()실제 질문을 사용 하는 JVM의 경우 "pthread_cond_wait에 가짜 웨이크 업이 발생하는 이유는 무엇입니까?" .
흐름

답변:


204

가짜 웨이크 업에 관한 Wikipedia 기사 에는 다음과 같은 내용이 있습니다.

pthread_cond_wait()Linux 의 기능은 futex시스템 호출을 사용하여 구현됩니다 . EINTR프로세스가 신호를 수신하면 Linux의 각 차단 시스템 호출이 갑자기 리턴 됩니다. ... 시스템 호출 pthread_cond_wait()이 아닌 시간에 실제 깨우기를 놓칠 수 있으므로 대기를 다시 시작할 수 없습니다 futex. 이 경쟁 조건은 호출자가 불변 값을 확인해야만 피할 수 있습니다. 따라서 POSIX 신호는 가짜 웨이크 업을 생성합니다.

요약 : 리눅스 프로세스가 신호를 받으면 대기중인 스레드는 각각 멋진 핫 스퓨리어스 웨이크 업을 즐깁니다 .

나는 그것을 산다. 일반적으로 모호한 "성능을위한"이유보다 삼키기 쉬운 피임약입니다.


13

3
이 EINTR 차단 해제는 Unix 파생 시스템의 모든 차단 시스템 호출에 적용됩니다. 이로 인해 커널이 훨씬 간단 해졌지만 애플리케이션 프로그래머가 부담을지게되었습니다.
팀 윌리스 크로프트

2
pthread_cond_wait () 및 친구가 EINTR을 반환 할 수 없다고 생각했지만 가짜로 깨어났다면 0을 반환합니까? From : pubs.opengroup.org/onlinepubs/7908799/xsh/… "이 함수는 [EINTR]의 오류 코드를 반환하지 않습니다."
gubby

2
@jgubby 맞습니다. 기본 futex()호출은을 반환 EINTR하지만 그 반환 값은 다음 수준으로 올라가지 않습니다. 따라서 pthread 호출자는 불변을 확인해야합니다. 그들이 말하는 것은 pthread_cond_wait()반환이 대기 상태가 잘못 깨어 났기 때문에 루프 조건 (불변)을 다시 확인해야한다는 것입니다. 시스템 호출 중에 신호를 수신하는 것이 가능한 원인 중 하나 일뿐입니다.
John Kugelman

1
아마도, pthread라이브러리는 그 책임을 사용자에게 넘기는 대신 가짜 웨이크 업을 제거하기 위해 자체 불변 및 자체 검사 로직을 제공 할 수 있습니다. 그것은 아마도 주장 된 성능 영향을 미칠 것입니다.

22

이 동작을 보여주는 생산 시스템이 있습니다. 스레드는 큐에 메시지가 있다는 신호를 기다립니다. 사용량이 많은 기간에는 최대 20 %의 웨이크 업이 의심됩니다 (즉, 웨이크 업시 큐에 아무것도 없음). 이 스레드는 메시지의 유일한 소비자입니다. Linux SLES-10 8 프로세서 박스에서 실행되며 GCC 4.1.2로 빌드됩니다. 시스템이 메시지를 충분히 빨리 읽지 못하면 문제가 있기 때문에 메시지는 외부 소스에서 가져오고 비동기 적으로 처리됩니다.


15

제목의 질문에 대답하려면- 그렇습니다! 그것은 happen.Though는 않습니다 위키 문서는 내가 통해 들어와 다음 있는지 의사를 깨우는에 대해 동일한에 대한 좋은 설명을 좋은 거래를 언급 -

스레드 스케줄러는 기본 하드웨어 / 소프트웨어에서 비정상적인 문제로 인해 일시적인 정전이 발생할 수 있습니다. 물론, 이것이 가능한 한 드물게 발생하도록주의를 기울여야하지만 100 % 강력한 소프트웨어와 같은 것은 없기 때문에 스케줄러가이를 감지 할 경우 이러한 상황이 발생할 수 있고 정상적인 복구에주의를 기울이는 것이 합리적입니다 (예 : 누락 된 하트 비트를 관찰하여).

이제 스케줄러가 정전 중에 대기 스레드에 알리는 일부 신호가 누락 될 수 있다는 점을 고려하여 어떻게 스케줄러를 복구 할 수 있습니까? 스케줄러가 아무 것도하지 않으면 언급 된 "불운"스레드가 중단되어 영원히 대기합니다.이를 피하기 위해 스케줄러는 단순히 모든 대기 스레드에 신호를 보냅니다.

따라서 대기중인 스레드에 이유없이 통지 할 수있는 "계약"을 설정해야합니다. 정확히 말하면, 스케줄러 정전이 발생하는 이유가 있지만 스레드가 스케줄러 내부 구현 세부 사항에 대해 알 수 없도록 설계되었으므로이 이유는 "스퓨리어스"로 표시하는 것이 좋습니다.

나는 출처 에서이 답변을 읽었 으며 충분히 합리적이라고 생각했습니다. 또한 읽으십시오

자바에서 가짜 웨이크 업과이를 피하는 방법 .

추신 : 위의 링크는 가짜 모닝콜에 대한 추가 세부 정보가있는 개인 블로그로 연결됩니다.


9

Cameron Purdy블로그 게시물을 작성했습니다 가짜 모닝콜 문제에 부딪 치는 것에 대해 . 네, 그래요

Java가 배포되는 일부 플랫폼의 제한 때문에 사양에 있다고 생각합니다. 내가 틀렸을 수도 있지만!


나는 게시물을 읽고 무작위 / 결정적으로 깨어남으로써 하나의 응용 프로그램이 루핑 대기 패러다임을 준수하는지 테스트하기위한 단위 테스트를하는 것에 대한 아이디어를주었습니다. 아니면 이미 어딘가에 있습니까?
akarnokd

SO에 대한 또 다른 질문입니다. " 테스트에 사용할 수 있는 엄격한 VM이 있습니까?" 스레드 로컬 메모리가 엄격한 것을보고 싶습니다. 아직 존재하지는 않습니다.
oxbow_lakes

8

이것을 추가하십시오. 그렇습니다. 24 코어 시스템 (JDK 6)에서 멀티 스레딩 문제의 원인을 검색하는 데 3 일이 걸렸습니다. 10 건 중 4 건은 아무런 패턴없이 그 경험을했습니다. 이것은 2 코어 또는 8 코어에서 발생하지 않았습니다.

일부 온라인 자료를 연구했으며 이는 Java 문제가 아니라 일반적이지만 드물지만 예상되는 동작입니다.


안녕하세요 ReneS, 거기서 실행중인 앱을 개발하고 있습니까? java doc docs.oracle.com/javase/6/docs/api/java/lang/… 에서 제안 된 것처럼 외부 조건을 루프 검사하는 동안 호출하는 wait () 메소드가 있습니까?
gumkins

나는 그것에 대해 썼고 해결책은 조건 검사가있는 while 루프입니다. 제 실수는 빠진 루프 였지만 ...이 깨어남에 대해 배웠습니다. 두 코어에서는 절대로 24 코어가 아닙니다. blog.xceptance.com/2011/05/06/spurious-wakeup-the-rare-event
ReneS

40+ 코어 유닉스 서버에서 응용 프로그램을 실행할 때 비슷한 경험을했습니다. 엄청난 양의 가짜 모닝콜이있었습니다. -스퓨리어스 웨이크의 양이 시스템의 프로세서 코어의 양에 직접 비례하는 것처럼 보입니다.
bvdb

0

https://stackoverflow.com/a/1461956/14731 에는 기본 운영 체제가 트리거하지 않는 경우에도 가짜 웨이크 업으로부터 보호해야하는 이유에 대한 훌륭한 설명이 포함되어 있습니다. 이 설명은 Java를 포함한 여러 프로그래밍 언어에 적용됩니다.


0

OP의 질문에 답변

무작위 이벤트를 영원히 기다리지 않고이 대기를 깨우려면 어떻게해야합니까?

, 어떤 가짜 모닝콜 도이 기다리는 스레드를 깨울 수 없습니다 !

에 관계없이 의사를 깨우는가 또는 영업의의 경우, 특정 플랫폼에서 일어날 수 있는지 여부의는 긍정적이다 니펫을 불가능 에 대한 Condition.await()반환하고 라인을보고 "가짜 웨이크 업!" 출력 스트림에서.

매우 이국적인 Java 클래스 라이브러리를 사용하지 않는 한

표준 때문이다 오픈 JDKReentrantLock의 방법 newCondition()반환 AbstractQueuedSynchronizer'의의 구현 Condition중첩 된 인터페이스, ConditionObject(그런데, 그것의 유일한 구현 Condition이 클래스 라이브러리 인터페이스), 그리고 ConditionObject의 방법 await()조건이되지 않습니다 여부를 자체 점검 보류하고 가짜 웨이크 업으로 인해이 메소드가 실수로 리턴되지 않을 수 있습니다.

그건 그렇고, 일단 AbstractQueuedSynchronizer기반 구현이 포함 되면 가짜 모닝콜을 모방하기가 쉽기 때문에 직접 확인할 수 있습니다 . AbstractQueuedSynchronizer낮은 수준의 사용 LockSupportparkunpark방법, 그리고 경우에있는 invoke 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");
    }
}

unparking (main) 스레드가 대기중인 스레드를 깨우려고 시도해도 아무리 어려운 경우에도이 Condition.await()메서드는 반환되지 않습니다.

Condition대기중인 메소드 에 대한 가짜 깨우기 Condition인터페이스javadoc 에서 논의됩니다 . 그렇게 말하지만

조건을 기다릴 때 가짜 웨이크 업이 발생할 수 있습니다

그리고

응용 프로그램 프로그래머는 항상 발생할 수 있다고 가정하므로 항상 루프에서 기다리십시오.

하지만 나중에 추가합니다

가짜 웨이크 업 가능성을 제거하는 구현은 자유 롭다

그리고 인터페이스 AbstractQueuedSynchronizer의 구현 은 스퓨리어스 웨이크 업의 가능성을 제거합니다 .Condition

이것은 다른 ConditionObject대기중인 방법에 대해서도 마찬가지입니다 .

그래서, 결론은 다음과 같습니다

우리는 항상 Condition.await루프를 호출 하고 조건이 유지되지 않는지 확인해야하지만 표준 OpenJDK를 사용하면 Java 클래스 라이브러리가 발생할 수 없습니다 . 다시 한 번, 매우 특이한 Java 클래스 라이브러리를 사용하지 않는 한 (현재 거의 멸종 된 GNU ClasspathApache Harmony 로 알려진 다른 OpenJDK Java 클래스 라이브러리 는 표준 Condition인터페이스의 표준 구현과 동일한 것으로 보이므로 매우 예외적 임 )

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