[java] 자바의 휘발성과 정적

static모든 객체에 대해 volatile하나의 값 사본을 의미하고 모든 스레드에 대해 하나 의 값 사본 을 의미 한다고 말하는 것이 맞 습니까?

어쨌든 static변수 값도 모든 스레드에 대해 하나의 값이 될 것입니다. 그렇다면 왜 우리는 가야 volatile합니까?



답변

Java에서 정적 변수를 선언 한다는 것은 클래스의 오브젝트 수에 관계없이 사본이 하나만 있음을 의미합니다. 변수를 전혀 Objects작성 하지 않아도 변수에 액세스 할 수 있습니다 . 그러나 스레드는 로컬로 캐시 된 값을 가질 수 있습니다.

변수가 휘발성 이고 정적 이 아닌 경우 각 변수마다 하나의 변수가 있습니다 Object. 따라서 표면에는 일반 변수와 차이가 없지만 static과 완전히 다른 것처럼 보입니다 . 그러나 Object필드가 있어도 스레드는 로컬로 변수 값을 캐시 할 수 있습니다.

즉, 두 스레드가 동일한 Object의 변수를 동시에 업데이트하고 변수가 일시적으로 선언되지 않은 경우 스레드 중 하나가 이전 값을 캐시하는 경우가있을 수 있습니다.

여러 스레드를 통해 정적 값에 액세스하더라도 각 스레드는 로컬 캐시 사본을 가질 수 있습니다! 이를 피하기 위해 변수를 static volatile 로 선언 하면 스레드가 전역 값을 읽을 때마다 스레드를 읽습니다.

그러나 휘발성 은 적절한 동기화를 대신하지 않습니다!
예를 들어 :

private static volatile int counter = 0;

private void concurrentMethodWrong() {
  counter = counter + 5;
  //do something
  counter = counter - 5;
}

concurrentMethodWrong여러 번 동시에 실행 하면 카운터의 최종 값이 0과 다를 수 있습니다!
문제를 해결하려면 잠금을 구현해야합니다.

private static final Object counterLock = new Object();

private static volatile int counter = 0;

private void concurrentMethodRight() {
  synchronized (counterLock) {
    counter = counter + 5;
  }
  //do something
  synchronized (counterLock) {
    counter = counter - 5;
  }
}

또는 AtomicInteger수업을 사용하십시오 .


답변

정적과 휘발성의 차이점 :

정적 변수 : 두 개의 스레드 (가정하는 경우 t1t2) 같은 개체에 액세스하고 그 의미 다음 static으로 선언 된 변수를 업데이트하는 t1t2각각의 캐시 (정적 변수 포함) 동일한 개체의 자체 로컬 복사본을 만들 수 있습니다, 그래서 갱신 t1로컬 캐시의 정적 변수에 의해 만들어진 캐시의 정적 변수에는 반영되지 않습니다 t2.

정적 변수는에 사용되는 객체의 컨텍스트 같은 클래스의 다른 모든 객체에 반영 업데이트가 하나의 객체에 의해 하지만 스레드의 맥락에서 즉시 정적 변수에 하나 개의 스레드의 업데이트는 변경 사항을 반영 할 경우 모든 스레드 (로컬 캐시에 있음).

휘발성 변수 : 두 개의 스레드 (가정하는 경우 t1t2) 같은 개체에 액세스하고 그 의미 다음 volatile로 선언 된 변수를 업데이트 할 t1t2개체의 자체 로컬 캐시를 만들 수 휘발성으로 선언 된 변수를 제외하고를 . 따라서 휘발성 변수에는 다른 스레드에 의해 업데이트되고 하나의 스레드에서 휘발성 변수에 대한 업데이트가 즉시 다른 스레드에 반영되는 하나의 기본 사본이 있습니다.


답변

다른 답변 외에도 이미지 하나를 추가하고 싶습니다 (pic은 이해하기 쉽습니다)

여기에 이미지 설명을 입력하십시오

static변수는 개별 스레드에 대해 캐시 될 수 있습니다. 다중 스레드 환경에서 한 스레드가 캐시 된 데이터를 수정하면 다른 스레드에는 사본있으므로 반영되지 않을 수 있습니다.

volatile선언은 스레드 가 데이터를 캐시하지 않고 공유 사본사용하도록 합니다.

이미지 소스


답변

내 생각 staticvolatile전혀 관계가 없습니다. 나는 당신이 원자 접근 을 이해하기 위해 자바 튜토리얼을 읽고 , 왜 원자 접근을 사용하고, 인터리브 된 것을 이해하는지 , 당신은 답을 찾을 것이라고 제안한다 .


답변

간단히 말해서

  1. static : static변수는 객체가 아니라 클래스 와 연결됩니다 . 클래스의 모든 인스턴스는 클래스 변수를 공유합니다. 클래스 변수는 메모리의 고정 된 한 위치에 있습니다.

  2. volatile :이 키워드는 클래스인스턴스 변수 모두에 적용 가능 합니다.

휘발성 변수를 사용하면 휘발성 변수에 대한 쓰기가 동일한 변수의 후속 읽기와의 관계를 확립하기 때문에 메모리 일관성 오류의 위험이 줄어 듭니다. 이것은 휘발성 변수에 대한 변경 사항이 항상 다른 스레드에서 볼 수 있음을 의미합니다

휘발성 변수를 더 잘 이해하기 위해이 기사 를 살펴보십시오 Javin Paul.

여기에 이미지 설명을 입력하십시오

volatile키워드가 없으면 각 스레드 스택의 변수 값이 다를 수 있습니다. 변수를로 설정 volatile하면 모든 스레드가 작업 메모리에서 동일한 값을 가져오고 메모리 일관성 오류를 피할 수 있습니다.

여기서 용어 variablestatic(클래스) 변수 또는 instance(객체) 변수 일 수 있습니다.

귀하의 질문에 관하여 :

어쨌든 정적 변수 값도 모든 스레드에 대해 하나의 값이 될 것입니다. 그렇다면 왜 휘발성을 사용해야합니까?

instance내 응용 프로그램에서 변수가 필요한 경우 변수를 사용할 수 없습니다 static. static변수의 경우에도 다이어그램에 표시된대로 스레드 캐시로 인해 일관성이 보장되지 않습니다.

volatile변수를 사용하면 휘발성 변수에 대한 쓰기가 동일한 변수의 후속 읽기와의 관계를 확립하기 때문에 메모리 일관성 오류의 위험이 줄어 듭니다. 이는 휘발성 변수에 대한 변경 사항이 항상 다른 스레드에 표시됨을 의미합니다.

또한 스레드가 휘발성 변수를 읽을 때 휘발성에 대한 최신 변경 사항뿐만 아니라 변경을 초래 한 코드의 부작용 => 메모리 일관성 오류가 여전히 휘발성 변수로 가능하다는 것을 의미합니다. . 부작용을 피하려면 동기화 된 변수를 사용해야합니다. 그러나 Java에는 더 나은 솔루션이 있습니다.

동기화 된 코드를 통해 이러한 변수에 액세스하는 것보다 간단한 원자 변수 액세스를 사용하는 것이 더 효율적입니다.

수업 중 일부 java.util.concurrent패키지 동기화에 의존하지 않는 원자 적 메소드를 제공합니다.

자세한 내용은이 높은 수준의 동시성 제어 문서를 참조하십시오.

특히 원자 변수를 살펴보십시오 .

관련 SE 질문 :

휘발성 대 원자

휘발성 부울 대 원자 부울

Java에서 휘발성과 동기화의 차이점


답변

휘발성 변수 값 액세스는 주 메모리에서 직접 이루어집니다. 멀티 스레딩 환경에서만 사용해야합니다. 정적 변수는 한 번로드됩니다. 단일 스레드 환경에서 사용되는 경우 변수 사본이 업데이트 되더라도 스레드가 하나만 있으므로 액세스하는 데 아무런 해가 없습니다.

이제 정적 변수가 다중 스레딩 환경에서 사용되면 원하는 결과를 기대하면 문제가 발생합니다. 각 스레드마다 고유 한 사본이 있으므로 한 스레드에서 정적 변수를 늘리거나 줄이면 다른 스레드에 반영되지 않을 수 있습니다.

정적 변수에서 원하는 결과를 기대하면 멀티 스레딩에서 static과 함께 volatile을 사용하면 모든 것이 해결됩니다.


답변

정적 변수가 스레드 로컬 메모리에 캐시되어 있는지 또는 NOT인지 확실하지 않습니다. 그러나 동일한 객체 (obj)에 액세스하는 두 개의 스레드 (T1, T2)를 실행하고 T1 스레드가 정적 변수로 업데이트하면 T2에 반영되었습니다.