2011-01-23 3 views
5

http://www.javamex.com/tutorials/synchronization_volatile.shtml을 참조하면 다음과 같은 경우 키워드를 사용해야합니까? 추가 규칙으로 인해 volatile 키워드를 사용해야하는지 잘 모르겠습니다. 3.2 개의 다른 쓰기 및 읽기 스레드가 동시에 활성화되지 않으면 휘발성을 사용해야합니까?

  1. 프리미티브 정적 변수는 스레드 A로 작성 될 것이다
  2. 같은 프리미티브 정적 변수는 스레드 B에 의해 판독 될
  3. 만 실행
  4. 스레드 B, 스레드 A는 "죽은 후 ". 그것은 "죽음"후,

는 스레드 A가 작성한 새 값이 항상 메인 메모리에 전념 할 것이다 ("죽은"수단, 스레드 A의 무효 실행의 마지막 문장은 완료)? 그렇다면 위의 3 가지 조건이 충족 될 경우 volatile 키워드가 필요하지 않음을 의미합니까?

이 경우 volatile이 필요하다고 생각됩니다. 필요한 경우 ArrayList이 손상되었을 수 있습니다. 하나의 스레드가 구성원 변수 인 size을 삽입하고 업데이트 할 수 있습니다. 나중에 다른 스레드 (동시에 사용하지 않음)는 ArrayListsize을 읽을 수 있습니다. ArrayList 소스 코드를 보면 size이 (가) volatile로 선언되지 않고 있습니다.

ArrayList의의 JavaDoc에서 만 복수의 thread가 동시에 ArrayList에 인스턴스에 액세스하기위한 ArrayList가 사용하기에 안전이 아니라고 언급하지만,하지 않는 여러 스레드가 서로 다른 타이밍에서 ArrayList의 인스턴스에 액세스 할 수 있습니다.

는 여전히 휘발성 (동기화 또는 다른 형태)를 사용할 필요가, 내가이 문제

public static void main(String[] args) throws InterruptedException { 
    // Create and start the thread 
    final ArrayList<String> list = new ArrayList<String>(); 
    Thread writeThread = new Thread(new Runnable() { 
     public void run() { 
      list.add("hello"); 
     } 
    }); 
    writeThread.join(); 
    Thread readThread = new Thread(new Runnable() { 
     public void run() { 
      // Does it guarantee that list.size will always return 1, as this list 
      // is manipulated by different thread? 
      // Take note that, within implementation of ArrayList, member 
      // variable size is not marked as volatile. 
      assert(1 == list.size()); 
     } 
    }); 
    readThread.join(); 
} 
+2

확실한 질문은 왜 누군가가 2 개의 스레드를 사용하게 될까요? 그냥 학문? –

답변

6

예를 issulate하려면 다음 코드를 사용하자.

2 개의 스레드가 서로 다른 프로세서에서 실행될 수 있고 다른 스레드가 시작되기 전에 한 스레드가 오래 끝난 경우에도 두 번째 스레드가 읽을 때 가장 새로운 값을 얻을 것이라는 보장이 없습니다. 필드가 휘발성으로 표시되지 않고 다른 동기화가 사용되지 않으면 두 번째 스레드는 실행중인 프로세서에서 로컬로 캐시 된 값을 가져올 수 있습니다. 캐시 된 값은 이론적으로는 첫 번째 스레드가 완료된 후를 포함하여 오랜 기간 동안 오래된 것일 수 있습니다.

volatile을 사용하면 값은 항상 프로세서의 캐시 된 값을 무시하고 주 메모리에 쓰여지고이 메모리에서 읽습니다.

+0

동기화 및 휘발성 스레드간에 데이터를 전달하는 _only_ 방법이 아닙니다. 내 대답은 아래 참조하십시오. – jtahlborn

+0

네, 저도 압니다 ...하지만 그 질문을 읽을 때, 옌 청 챙은 구체적으로 어떻게 휘발성이 작동하는지 알고 싶어합니다. 저는 구체적으로 제가 휘발성 문제를 다루고 있음을 분명히하기 위해 제 대답을 다시 말해 왔습니다. –

+0

휘발성의 필요성을 의심합니다. 업데이트 된 질문을 참조하십시오. –

1

수동으로 메모리 장벽을 만들지 않는 한 가능합니다. A가 변수를 설정하고 B가 레지스트리에서 oit을 가져 오기로 결정하면 문제가 발생합니다. 따라서 암시적인 장벽 (암시 적 (잠금, 휘발성) 또는 명시 적)이 필요합니다.

-1

하면 스레드 A는 확실히 스레드 B는 다음 읽기 시작 전에 휘발성

예를 들어, 사용하지 않는 것이 가능할 것이다 죽는다.

public class MyClass { 

    volatile int x = 0; 

    public static void main(String[] args) { 

     final int i = x; 
     new Thread() { 
     int j = i; 
     public void run() { 
      j = 10; 
      final int k = j; 
      new Thread() { 
       public void run() { 
        MyClass.x = k; 
       }    
      }.start(); 
     } 
     }.start(); 
    } 
} 

그러나 문제는 스레드가 스레드 B가 이제 스레드 A가 작성되는 값이 변경된 것을 필요로하고 자신의 캐시 된 버전을 사용하지 시작 중이다. 이렇게하는 가장 쉬운 방법은 스레드 A가 스레드 B를 생성하도록하는 것입니다.그러나 스레드 A가 스레드 B를 생성 할 때 수행 할 다른 작업이 없다면 이는 약간의 무의미한 것입니다 (왜 같은 스레드를 사용하지 않는 것이 좋을까요?).

또 다른 대안은이 변수에 의존하는 다른 스레드가 없다면 스레드 A가 휘발성 변수로 로컬 변수를 초기화하고 필요한 작업을 수행 한 다음 마지막으로 로컬 변수의 내용을 다시 쓸 수 있다는 것입니다 휘발성 변수. 그런 다음 스레드 B가 시작될 때 휘발성 변수에서 로컬 변수를 초기화하고 그 후에 로컬 변수에서만 읽습니다. 이렇게하면 휘발성 변수를 동기화하는 데 소요되는 시간을 크게 줄일 수 있습니다. 이 솔루션이 (다른 스레드가 휘발성 변수에 쓰는 등으로 인해) 받아 들일 수없는 것처럼 보이면 변수 volatile을 반드시 선언해야합니다.

3

아니요, 필요하지 않을 수도 있습니다. 마크 바이어스의 답변이 상당히 정확하기는하지만 제한적입니다. 동기화 및 휘발성은 스레드간에 정확하게 데이터를 전달하는 방법이 아닙니다. "동기화 지점"에 대해 다른 얘기는 거의 없습니다. 특히 스레드 시작 및 스레드 끝 동기화 지점입니다. 그러나 스레드 B를 시작하는 스레드는 스레드 A가 완료되었음을 인식 했어야합니다 (예 : 스레드에 결합하거나 스레드의 상태를 확인). 이 경우 해당 변수는 변동될 필요가 없습니다.

+0

휘발성의 필요성을 의심합니다. 업데이트 된 질문을 참조하십시오. –

+0

@Yan - 스레드 액세스가 동시에 발생하는지 여부는 중요하지 않습니다. 올바른 동기화없이 스레드를 통한 액세스가 끊어집니다. 그러나 내가 언급 한 바와 같이, 내가 언급 한 기준을 충족 시키면 스레드 끝/스레드 시작과 유효한 동기화 지점을 가질 수 있습니다. – jtahlborn

+0

@ Yan - 예제 코드는 내가 언급 한 기준을 충족하므로 휘발성이 필요하지 않습니다. – jtahlborn

1

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4

스레드 T1 의 마지막 동작은 동기화- 와 어떠한 조치도 T1이 종료되었음을 감지 다른 스레드 T2. T2는 T1.isAlive() 또는 T1.join()을 호출하여 을 수행 할 수 있습니다.

휘발성을 사용하지 않고 목표를 달성 할 수 있습니다.

많은 경우, 명백한 시간 의존성이있는 경우 동기화가 후드 사용자에 의해 수행되고 응용 프로그램에 추가 동기화가 필요하지 않습니다. 불행히도 이것이 규칙이 아니기 때문에 프로그래머는 각 사례를 신중하게 분석해야합니다.

예를 들어 스윙 작업자 스레드입니다. 사람들은 작업자 스레드에서 계산을 수행하고 결과를 변수에 저장 한 다음 이벤트를 발생시킵니다. 이벤트 스레드는 변수에서 계산 결과를 읽습니다. "이벤트 발생"은 이미 동기화를했기 때문에 응용 프로그램 코드에서 명시적인 동기화가 필요하지 않으므로 작업 스레드의 기록은 이벤트 스레드에서 볼 수 있습니다.

한편, 이것은 희열이 있습니다. 반면에 많은 사람들은 이것을 이해하지 못했고 단순히 문제를 생각하지 않았기 때문에 동기화를 생략했습니다. 그들의 프로그램은 이번에는 정확합니다.