자바의 휘발성과 정적


265

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

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


답변:


365

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수업을 사용하십시오 .


7
휘발성 수정자는 필드를 읽는 모든 스레드가 가장 최근에 작성된 값을 볼 수 있도록 보장하므로 변수가 여러 스레드에서 공유되고이 기능이 필요한 경우 사용 사례에 따라 다릅니다.
stivlo

5
"로컬 캐시"라고 할 때 캐시는 무엇입니까? CPU 캐시, 어떤 종류의 JVM 캐시?
mert inan

6
@mertinan 예, 변수는 프로세서 또는 코어에 가까운 캐시에있을 수 있습니다. 자세한 내용은 cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 을 참조하십시오.
stivlo

15
'휘발성'은 '객체 당 하나의 변수'를 의미 하지 않습니다 . '정적'이 없으면 그렇게합니다. OP 부분에서이 기본적인 오해를 해결하지 못한 경우 -1.
Lorne의 후작

27
@EJP 나는 "각 개체에 대해 하나 개의 변수가있을 것입니다, 휘발성 등의 변수를 선언. 그래서 표면에 정상적인 변수에서 차이가없는 것 같다"문장이 생각이 설명했다, 내가 추가 한 정적하지 , 기사를 편집하고 문구를 개선하여 더 명확하게 만드십시오.
stivlo

288

정적과 휘발성의 차이점 :

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

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

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


6
안녕하세요 @Som, 내가 틀렸다면 정정하십시오. 그러나 정적 변수에 대한 하나의 스레드 업데이트가 로컬 캐시의 모든 스레드에 대한 변경 사항을 즉시 반영하는 스레드 컨텍스트에서는 "문이 아니라고 생각하지 마십시오 . " 정적 변수에 대한 하나의 스레드 업데이트는 로컬 캐시에있는 모든 스레드에 대한 변경 사항을 즉시 << NOT >> 반영합니다. "
Jaikrat 2016 년

@Jaikrat 네, 저에게는 매우 혼란 스러웠습니다. 내 이해는 당신이 옳고이 답변이 서면으로 잘못되었다는 것입니다. 내가 틀렸다면 수정하고 싶습니다.
스튜어트

@Jaikrat 스레드는 정적 변수를 캐시하지 않지만 업데이트 된 정적 변수를 참조하십시오.
Som

@Som 그런 다음 para를 수정하고 Thread의 컨텍스트에서 제거 하고 싶지는 않습니다 . 매우 혼란 스럽습니다. 감사합니다
Jaikrat

슬프게도이 답변은 틀 렸습니다. 최신 CPU에서는 volatile고유 한 CPU 캐시간에 변수도 공유 할 수 있습니다. 캐시가 수정하기 전에 캐시 라인의 독점 소유권을 협상하기 때문에 문제가되지 않습니다.
David Schwartz 1

32

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

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

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

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

이미지 소스


1
정적 변수는 스레드 아래의 객체간에 공유됩니까? 정적 변수는 스레드와 상관없이 모든 객체에서 공유됩니다.
cquezel

1
"휘발성 변수는 여러 스레드간에 공유되므로 개체도 마찬가지입니다." 휘발성은 여러 스레드 또는 객체간에 변수를 공유하는 방법을 변경하지 않습니다. 런타임이 값을 캐시하는 방법을 변경합니다.
cquezel

1
정적 변수에 대한 귀하의 의견은 비 정적에 적용되며 "캐시됩니다"및 "반영하지 않을 것"은 "캐시 될 수 있음"및 "반영되지 않을 수 있음"으로 표시되어야합니다.
cquezel

4
나는 매우 혼란 스러웠다. 이 사진은 모든 질문을 해결했습니다!
vins

5

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


4

간단히 말해서

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

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

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

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

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

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

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

귀하의 질문에 관하여 :

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

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

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

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

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

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

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

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

관련 SE 질문 :

휘발성 대 원자

휘발성 부울 대 원자 부울

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


이 답변에 정말 감사합니다. 나는 volatile이전 이 무엇인지 알았지 만,이 대답은 왜 변수 volatile와 함께 사용해야합니까? static
Chaklader Asfak Arefe

volatile :이 키워드는 클래스 및 인스턴스 변수 모두에 적용 가능합니다. 클래스에 적용 할 수있는 위에서 언급 한 내용이 잘못되었습니다. 변수에 적용 할 수있는 두 가지 핵심 단어 만 휘발성과 일시적입니다. 휘발성은 수업에 적용되지 않습니다.
ASR

volatile은 클래스 (static) 변수에 적용 가능합니다. Google에서 이중 잠금 싱글 톤 링크를 확인하면 이해가 잘못되었음을 알 수 있습니다. stackoverflow.com/questions/18093735/…
Ravindra babu

개인 정적 휘발성은 유효한 선언입니다.
Ravindra babu

0

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

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

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


0

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


-2

변수를 정적으로 선언하면 변수 사본이 하나만 존재합니다. 따라서 다른 스레드가 해당 변수에 액세스 할 때마다 변수에 대한 최종 값은 하나만 있습니다 (변수에 할당 된 메모리 위치가 하나뿐이므로).

변수가 휘발성으로 선언 된 경우 모든 스레드에는 고유 한 변수 사본이 있지만 값은 기본 메모리에서 가져 오므로 모든 스레드의 변수 값은 동일합니다.

따라서 두 경우 모두 변수 값이 모든 스레드에서 동일하다는 것이 핵심입니다.


15
변수가 휘발성으로 선언되면 모든 스레드에는 고유 한 변수 사본이 있지만 값은 기본 메모리에서 가져옵니다. => 맞습니다. 따라서 모든 스레드에서 변수의 값은 동일합니다. => 잘못된 경우 각 스레드는 동일한 Object에 대해 동일한 값을 사용하지만 각 Object에는 고유 한 사본이 있습니다.
stivlo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.