programing

휘발성 롱을 사용하는 데 어떤 의미가 있습니까?

copysource 2021. 1. 17. 11:48
반응형

휘발성 롱을 사용하는 데 어떤 의미가 있습니까?


volatile두 개의 스레드에서 읽기 / 쓰기가 있고 잠금을 해제하는 오버 헤드 (또는 잠재적 교착 상태 위험)를 원하지 않는 경우 인스턴스 변수를 가끔 사용합니다 . 예를 들어 타이머 스레드는 일부 클래스에서 getter로 노출되는 int ID를 주기적으로 업데이트합니다.

public class MyClass {
  private volatile int id;

  public MyClass() {
    ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
    execService.scheduleAtFixedRate(new Runnable() {
      public void run() {
        ++id;
      }
    }, 0L, 30L, TimeUnit.SECONDS);
  }

  public int getId() {
    return id;
  }
}

내 질문 : JLS는 32 비트 원자 될 것입니다 읽 보장 점을 감안는 어떤 시점이 지금 휘발성 오래 사용은? (예 : 64 비트).

주의 사항 : volatileover 사용 synchronized은 사전 최적화의 경우 라고 답장하지 마십시오 . 나는 어떻게 / 언제 사용하는지 잘 알고 synchronized있지만 더 volatile바람직한 경우 가 있습니다. 예를 들어, 단일 스레드 애플리케이션에서 사용하기 위해 Spring Bean을 정의 할 때 volatileSpring 컨텍스트가 메인 스레드에서 각 Bean의 속성을 초기화한다는 보장이 없기 때문에 인스턴스 변수 를 선호하는 경향이 있습니다.


귀하의 질문을 올바르게 이해했는지 확실하지 않지만 JLS 8.3.1.4. 휘발성 필드 상태 :

필드는 휘발성으로 선언 될 수 있으며,이 경우 Java 메모리 모델은 모든 스레드가 변수에 대해 일관된 값을 볼 수 있도록합니다 ( §17.4 ).

그리고 아마도 더 중요한 것은 JLS 17.7 double 및 long의 비 원자 처리 :

17.7 double 및 long의 비원 자적 처리
[...]
Java 프로그래밍 언어 메모리 모델의 목적을 위해 비 휘발성 long 또는 double 값에 대한 단일 쓰기는 두 개의 개별 쓰기로 처리됩니다. 절반. 이로 인해 스레드가 한 쓰기에서 64 비트 값의 처음 32 비트를보고 다른 쓰기에서 두 번째 32 비트를 보는 상황이 발생할 수 있습니다. 휘발성 long 및 double 값의 쓰기 및 읽기는 항상 원자 적입니다. 참조에 대한 쓰기 및 읽기는 32 비트 또는 64 비트 값으로 구현되는지 여부에 관계없이 항상 원자 적입니다.

즉, "전체"변수는 두 부분뿐만 아니라 휘발성 수정 자에 의해 보호됩니다. 이것은 읽기조차도 비 휘발성 long / double에 대해 원자 적이 지 않기 때문에 s 에 대해 s 에 대해 휘발성을 사용하는 것이 훨씬 더 중요하다고 주장하도록 유혹합니다 .longint


이것은 예를 통해 입증 될 수 있습니다.

  • 두 개의 필드를 지속적으로 토글합니다. 하나는 휘발성으로 표시되고 다른 하나는 설정된 모든 비트와 모든 비트가 지워집니다.
  • 다른 스레드에서 필드 값 읽기
  • foo 필드 (volatile로 보호되지 않음)가 일관성없는 상태에서 읽을 수 있음을 확인하십시오. 이는 volatile로 보호되는 bar 필드에는 발생하지 않습니다.

암호

public class VolatileTest {
    private long foo;
    private volatile long bar;
    private static final long A = 0xffffffffffffffffl;
    private static final long B = 0;
    private int clock;
    public VolatileTest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    foo = clock % 2 == 0 ? A : B;
                    bar = clock % 2 == 0 ? A : B;
                    clock++;
                }
            }

        }).start();
        while (true) {
            long fooRead = foo;
            if (fooRead != A && fooRead != B) {
                System.err.println("foo incomplete write " + Long.toHexString(fooRead));
            }
            long barRead = bar;
            if (barRead != A && barRead != B) {
                System.err.println("bar incomplete write " + Long.toHexString(barRead));
            }
        }
    }

    public static void main(String[] args) {
        new VolatileTest();
    }
}

산출

foo incomplete write ffffffff00000000
foo incomplete write ffffffff00000000
foo incomplete write ffffffff
foo incomplete write ffffffff00000000

이것은 32 비트 VM에서 실행할 때만 발생합니다. 64 비트 VM에서는 몇 분 안에 단일 오류를 얻을 수 없습니다.


"휘발성"은 다양한 용도로 사용됩니다.

  • guarantees atomic writes to double/long
  • guarantees that when a thread A sees change in volatile variable made by thread B, thread A can also see all other changes made by thread B before the change to volatile variable (think setting the number of used cells in array after setting the cells themselves).
  • prevents compiler optimization based on assumption that only one thread can change the variable (think tight loop while (l != 0) {}.

Is there more?

ReferenceURL : https://stackoverflow.com/questions/3038203/is-there-any-point-in-using-a-volatile-long

반응형