빈 동기화 된 블록은 어떤 상황에서 올바른 스레딩 의미를 달성 할 수 있습니까?

StackOverflow https://stackoverflow.com/questions/686415

문제

나는 a FindBugs 내 코드 기반에 대한보고와 트리거 된 패턴 중 하나는 빈 용이었습니다. synchronzied 블록 (즉, 즉 synchronized (var) {}). 그만큼 문서화가 말합니다:

빈 동기화 된 블록은 대부분의 사람들이 인식하는 것보다 훨씬 미묘하고 사용하기 어렵고 빈 동기화 된 블록은 덜 고안된 솔루션보다 거의 더 나은 솔루션이 아닙니다.

내 경우에는 블록의 내용이 댓글을 달았 기 때문에 발생했지만 synchronized 성명서는 여전히 거기에있었습니다. 어떤 상황에서 비어있을 수 있습니다 synchronized 블록이 올바른 스레딩 시맨틱을 달성합니까?

도움이 되었습니까?

해결책

빈 동기화 된 블록은 다른 사람이 해당 동기화기를 사용할 때까지 기다립니다. 그것은 당신이 원하는 것일 수 있지만, 동기화 된 블록에서 후속 코드를 보호하지 않았기 때문에 다른 사람이 후속 코드를 실행하는 동안 기다리고있는 것을 수정하는 것을 막는 것은 없습니다. 그것은 당신이 원하는 것을 거의 결코 없습니다.

다른 팁

이전 답변은 빈에 대한 가장 유용한 것들을 강조하지 못합니다. synchronized 블록 : 스레드에서 가변 변경 및 기타 작업의 가시성을 보장 할 수 있습니다. 처럼 jtahlborn 나타나면, 동기화는 컴파일러에 "메모리 배리어"를 부과하여 캐시를 플러시하고 새로 고치도록 강요합니다. 그러나 나는“뱀이 논의한 곳”을 찾지 못했기 때문에 스스로 대답을 썼습니다.

int variable;

void test() // This code is INCORRECT
{
    new Thread( () ->  // A
    {
        variable = 9;
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

위의 프로그램이 잘못되었습니다. 변수의 값은 스레드 A 또는 B 또는 둘 다로 로컬로 캐시 될 수 있습니다. 따라서 B는 A가 쓴 9의 가치를 결코 읽지 못할 수도 있고, 따라서 영원히 고장 될 수 있습니다.

빈 사용으로 스레드에서 가변 변경을 보이게합니다. synchronized 블록

가능한 수정 사항 중 하나는 a를 추가하는 것입니다 volatile (효과적으로 "캐시 없음") 변수에 대한 수정 자. 그러나 때로는 변수의 캐싱을 완전히 금지하기 때문에 이것은 비효율적입니다. 비어 있는 synchronized 반면에 블록은 캐싱을 금지하지 않습니다. 그들이하는 모든 일은 캐시가 특정 임계 지점에서 메인 메모리와 동기화하도록 강요하는 것입니다. 예를 들어: *

int variable;

void test() // Corrected version
{
    new Thread( () ->  // A
    {
        variable = 9;
        synchronized( o ) {} // Flush to main memory
        for( ;; )
        {
            // Do other stuff
        }
    }).start();

    new Thread( () ->  // B
    {
        for( ;; )
        {
            synchronized( o ) {} // Refresh from main memory
            if( variable == 9 ) System.exit( 0 );
        }
    }).start();
}

final Object o = new Object();

메모리 모델이 가시성을 보장하는 방법

가시성을 보장하려면 두 스레드 모두 동일한 객체에 동기화해야합니다. 이 보증은 자바 메모리 모델, 특히 "모니터 m에서의 조치 잠금 해제 규칙에 따라 동기화됩니다 M "및 그에 따른 모든 후속 잠금 동작 앞서 발생합니다 그 행동. 그래서 A의 O의 잠금 해제 O의 모니터의 꼬리에 synchronized 블록은 블록의 헤드에서 B의 후속 잠금 이전에 발생합니다. (신체가 비워 질 수있는 이유를 설명하는 것은이 이상한 꼬리 헤드 순서입니다.) A의 글이 잠금 해제보다 우선하고 B의 잠금이 읽기보다 우선한다는 점을 감안할 때, 관계는 쓰기와 읽기를 모두 커버하기 위해 연장되어야합니다. 읽기 전에 쓰기가 발생합니다. 메모리 모델 측면에서 수정 된 프로그램을 정확하게 만드는 것은 중요한 확장 관계입니다.

나는 이것이 비어있는 가장 중요한 용도라고 생각합니다 synchronized 블록.


   * 나는 그것이 프로세서 캐싱의 문제인 것처럼 말합니다. 왜냐하면 그것이 그것을 볼 수있는 유용한 방법이라고 생각하기 때문입니다. 사실, Aleksandr Dubinsky는 다음과 같이 말했습니다. 관계는 CPU보다 컴파일러가 허용하는 일에 대한 관계가되기 전입니다. '

예전에는 사양이 특정 메모리 장벽 작업이 발생한 것을 암시 한 경우였습니다. 그러나 사양은 이제 변경되었으며 원래 사양은 올바르게 구현되지 않았습니다. 다른 스레드가 잠금을 해제하기를 기다리는 데 사용될 수 있지만 다른 스레드가 이미 잠금을 얻었음을 조정하면 까다로울 것입니다.

동기화는 단순히 기다리는 것보다 약간 더 많은 일을하는 반면, 무능한 코딩은 필요한 효과를 달성 할 수 있습니다.

에서 http://www.javaperformancetuning.com/news/qotm030.shtml

  1. 스레드는 객체에 대한 모니터의 잠금을 얻습니다 (모니터가 잠금 해제되었다고 가정하고 그렇지 않으면 스레드가 모니터가 잠금 해제 될 때까지 기다립니다).
  2. 스레드 메모리는 모든 변수를 플러시합니다. 즉, "메인"메모리에서 효과적으로 읽는 모든 변수가 있습니다 (JVMS는 더러운 세트를 사용하여이를 최적화하여 "더러운"변수 만 플러시되지만 개념적으로 동일합니다. 섹션을 참조하십시오. Java 언어 사양의 17.9).
  3. 코드 블록이 실행됩니다 (이 경우 리턴 값을 현재 값 I3로 설정하여 "기본"메모리에서 방금 재설정되었을 수 있음).
  4. (변수에 대한 모든 변경 사항은 일반적으로 "메인"메모리에 기록되지만 geti3 ()의 경우 변경 사항이 없습니다.)
  5. 스레드는 모니터의 잠금을 객체에 대해 출시합니다.

Java의 메모리 모델을 심층적으로 살펴보면 Google의 '프로그래밍 언어의 고급 주제'시리즈 에서이 비디오를 살펴보십시오.http://www.youtube.com/watch?v=1fx4zco0ziy

그것은 컴파일러가 코드에 대해 (이론적이지만 때로는 실제로) 할 수있는 일에 대한 정말 좋은 개요를 제공합니다. 진지한 Java 프로그래머를위한 필수품!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top