2012-06-12 3 views
14

인스 턴 스가 다중 스레드에서 사용되는 경우 개인 필드를 volatile으로 선언해야합니까?getter 및 setter가있는 java :`volatile` 전용 필드

if(!stopRequested) 
    while(true) 
     i++; 
:

import java.util.concurrent.TimeUnit; 

// Broken! - How long would you expect this program to run? 
public class StopThread { 
    private static boolean stopRequested; // works, if volatile is here 

    public static void main(String[] args) throws InterruptedException { 
     Thread backgroundThread = new Thread(new Runnable() { 
      public void run() { 
       int i = 0; 
       while (!stopRequested) 
        i++; 
      } 
     }); 
     backgroundThread.start(); 
     TimeUnit.SECONDS.sleep(1); 
     stopRequested = true; 
    } 
} 

의 설명은

while(!stopRequested) 
    i++; 

이런 식으로 뭔가에 최적화되어 있다고 말한다 : Effective Java에서

는 코드가 휘발성없이 작동하지 않습니다 예를 들어이

stopRequested의 추가 수정본이 보이지 않습니다. 배경 스레드에 의해, 그래서 그것은 영원히 반복됩니다. (BTW, 그 코드는 JRE7에 volatile없이 종료됩니다.)

지금이 클래스 고려해야 다음과 같이

public class Bean { 
    private boolean field = true; 

    public boolean getField() { 
     return field; 
    } 

    public void setField(boolean value) { 
     field = value; 
    } 
} 

및 스레드를 :

public class Worker implements Runnable { 
    private Bean b; 

    public Worker(Bean b) { 
     this.b = b; 
    } 

    @Override 
    public void run() { 
     while(b.getField()) { 
      System.err.println("Waiting..."); 
      try { Thread.sleep(1000); } 
      catch(InterruptedException ie) { return; } 
     } 
    } 
} 

위의 코드 휘발성 물질을 사용하지 않고 예상대로 작동 :

public class VolatileTest { 
    public static void main(String [] args) throws Exception { 
     Bean b = new Bean(); 

     Thread t = new Thread(new Worker(b)); 
     t.start(); 
     Thread.sleep(3000); 

     b.setField(false); // stops the child thread 
     System.err.println("Waiting the child thread to quit"); 
     t.join(); 
     // if the code gets, here the child thread is stopped 
     // and it really gets, with JRE7, 6 with -server, -client 
    } 
} 

나는 공공 세터 때문에 c라고 생각한다. ompiler/JVM은 getField()을 호출하는 코드를 절대로 최적화해서는 안되지만 this article에는 변경 가능한 스레드 안전 클래스를 만들기 위해 적용해야하는 "휘발성 빈"패턴 (패턴 # 4)이 있다고 표시됩니다. 업데이트 : 아마도이 기사는 IBM JVM에만 적용됩니까?

질문 : JLS의 어느 부분에서 공개 getters/setters가있는 private 프리미티브 필드가 volatile으로 명시되어야한다고 명시 적으로 또는 암시 적으로 말합니까?

오랜 질문 죄송합니다. 문제를 자세히 설명하려고했습니다. 뭔가 명확하지 않은 경우 알려주십시오. 감사.

+0

스레드를 취소하기 위해 해당 필드가 필요하지 않은 것처럼 스레드에서 인터럽트 된 플래그를 사용할 수 있습니다. –

+1

@ NathanHughes, 이러한 클래스는 단지 최소한의 예제이며, 실제 코드는 다르며 스레드 인터럽트가 필요하지 않습니다. – khachik

답변

4

는 그 코드가

JRE7

에 휘발성없이 종료 서로 다른 런타임 인수와 같은 응용 프로그램을 배포한다면 이것은 변경할 수 있습니다, BTW

를 해결하고자합니다. 호이 스팅은 JVM의 기본 구현이 반드시 필요한 것은 아니므로 하나에서 작동하고 다른 상태에서는 작동하지 않을 수 있습니다. 처럼 후자의 예를 실행에서 자바 컴파일러를 방지 아무것도

그렇게

@Override 
public void run() { 
    if(b.getField()){ 
     while(true) { 
      System.err.println("Waiting..."); 
      try { Thread.sleep(1000); } 
      catch(InterruptedException ie) { return; } 
     } 
    } 
} 

그것은 여전히 ​​순차적으로 일관이며, 따라서 자바의 보장을 유지 귀하의 질문에 대답하기 - 특별히 17.4.3 읽을 수 있습니다

각 스레드 t에 의해 수행 된 모든 스레드 간 조치 중, t의 프로그램 오더는 이러한 작업은 스레드 내부 스레드 의 의미에 따라 수행됩니다.

모든 액션 프로그램 순서와 일치하는 전체 순서 (실행 순서)에 발생하는 경우 동작의 세트를 순차적으로 일치하고, 또한, 변수 V의 각 판독 R은 쓴 값 본다 다음과 같이 v에 w를 쓰십시오 :

다른 말로하면 - 스레드가 컴파일러/메모리 순서와 관계없이 동일한 순서로 필드의 읽기 및 쓰기를 볼 수 있다면 순차적으로 일관된 것으로 간주됩니다.

9

질문 : JLS의 어느 부분이 공개 getters/setters가있는 개인 기본 필드를 휘발성 (volatile)으로 선언해야하는지 명시 적으로 또는 암시 적으로 말하고 있습니까?

JLS 메모리 모델은 getter/setter에 대해 신경 쓰지 않습니다. 그들은 메모리 모델의 관점에서 볼 때 아무런 행동도하지 않습니다. 공개 필드에 액세스 할 수도 있습니다. 메소드 호출 뒤에서 부울을 래핑해도 메모리 가시성에는 영향을 미치지 않습니다. 후자의 예는 행운에 의해 순전히 작동합니다.

인스턴스가 여러 스레드에서 사용되는 경우 개인 필드를 휘발성으로 선언해야합니까?

클래스 (콩)는 다중 스레드 환경에서 사용할 경우

, 당신은 어떻게 든 계정으로 그렇게해야합니다. 개인 필드를 volatile으로 만드는 것은 각 스레드가 해당 필드의 최신 값을 볼 수 있도록 보장하며, 부실 값을 캐시/최적화하지는 않습니다. 그러나 atomicity의 문제는 해결되지 않습니다.

The article you linked to은 JVM 사양을 준수하는 모든 JVM (JLS가 몸을 기울여야 함)에 적용됩니다. JVM 공급 업체, 버전, 플래그, 컴퓨터 및 OS, 프로그램을 실행하는 횟수 (HotSpot 최적화가 10000 회 실행 후 시작되는 경우가 많음) 등에 따라 다양한 결과를 얻을 수 있으므로 스펙을 이해하고 신중하게 준수해야합니다. 신뢰할 수있는 프로그램을 만들려면 규칙에 따르십시오. 이 경우 실험은 JVM이 스펙에서 벗어나지 않는 한 원하는 방식으로 작동 할 수 있고 모든 JVM에 모든 종류의 동적 최적화가 포함되어 있기 때문에 일이 어떻게 작동 하는지를 알기가 어렵습니다.

+0

감사합니다. 부울 변수 (또는 long/double을 제외한 다른 원시 변수)를 설정하는 것은 원 자성 연산입니다. 내 질문은 JSL이이 경우 코드 동작을 정의하는 방법에 관한 것입니다. 또는 그것이 의미하는 것이 명백한 지 어떻게 정의하지 않는지. – khachik

+0

JLS는 귀하의 경우에 특별한 것을 정의하지 않습니다. bean은 메소드 호출 뒤에있는 변수 일 뿐이며 [메모리 모델] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4)이 그대로 적용됩니다. 메소드 호출은 메모리 모델의 관점에서 볼 때 아무런 문제가 없다. 원 자성 (atomicity)이란 논리적으로 서로 의존하는 필드가있는 경우 bean이 일관성없는 상태로 끝날 수 있음을 의미합니다. 예를 들어 "start"시간은 "end"시간 이전이어야합니다. 그렇지 않으면 보장되지 않습니다. 그 2 개의 필드를 원자 적으로 변경하는기구. –

+0

그건 그렇고,'volatile ''long'과'double' 값의 쓰기와 읽기는 항상 원자 적입니다. JLS 17.7을 참조하십시오. –

4

아니요, 해당 코드는 정확하지 않습니다. JLS의 어떤 것에서도 이 휘발성으로 선언되어야한다고 나와 있지 않습니다. 그러나 코드가 멀티 스레드 환경에서 올바르게 작동하게하려면 가시성 규칙을 따라야합니다. 휘발성 및 동기화 성은 스레드간에 데이터를 올바르게 볼 수있게하는 두 가지 주요 기능입니다.

예를 들어, 멀티 스레드 코드 작성의 어려움은 다양한 형태의 잘못된 코드가 테스트에서 잘 작동한다는 것입니다. 멀티 스레드 테스트가 "성공"했다고해서 테스트가 올바른 코드라는 것을 의미하지는 않습니다.

특정 JLS 참조는 Happens Before 섹션 (및 나머지 페이지)을 참조하십시오.

"표준"스레드 안전 관용구를 익히는 영리한 새로운 방법을 생각해 본다면 일반적으로 잘못 생각할 수 있습니다. 나는 귀하의 질문에 대답하기 전에

+0

JSL에서 필드가 휘발성으로 선언되어야한다고 말하면 아무것도 선언하지 말아야한다는 것을 어떻게 알 수 있습니까? 휘발성으로? 다중 스레드 코드 테스트에 대한 귀하의 견해는 OK 이상이지만 JSL에 관한 질문이었습니다. – khachik

+0

@khachik - JLS는 코드를 스레드로부터 안전하게 만들기 위해해야 ​​할 일에 대한 구체적인 내용을 제공합니다. 내가 참조한 섹션에는 다양한 옵션이 있습니다. JLS에서는 스레드 안전을 위해 필요한 코드가 필요하지 않으므로 필드를 휘발성으로 만들 필요가 없습니다. 그렇지 않으면 모든 필드가 기본적으로 휘발성이됩니다. – jtahlborn

+0

. 그것은 내 질문에 대답하지 않습니다. – khachik