logo

Java의 휘발성 키워드

휘발성 키워드는 다른 스레드에서 변수 값을 수정하는 데 사용됩니다. 또한 클래스를 스레드로부터 안전하게 만드는 데에도 사용됩니다. 이는 여러 스레드가 문제 없이 동시에 클래스의 메서드와 인스턴스를 사용할 수 있음을 의미합니다. 휘발성 키워드는 기본 유형 또는 객체와 함께 사용할 수 있습니다.

Git으로 결제하기

휘발성 키워드는 변수 값을 캐시하지 않고 항상 주 메모리에서 변수를 읽습니다. 휘발성 키워드는 클래스나 메서드와 함께 사용할 수 없습니다. 그러나 변수와 함께 사용됩니다. 또한 가시성과 순서도 보장합니다. 이는 컴파일러가 코드를 재정렬하는 것을 방지합니다.

특정 장치 레지스터의 내용은 언제든지 변경될 수 있으므로 이러한 액세스가 컴파일러에 의해 최적화되지 않도록 휘발성 키워드가 필요합니다.

 class Test { static int var=5; } 

위의 예에서는 두 개의 스레드가 동일한 클래스에서 작업하고 있다고 가정합니다. 두 스레드 모두 서로 다른 프로세서에서 실행되며 각 스레드에는 var의 로컬 복사본이 있습니다. 스레드가 해당 값을 수정하는 경우 변경 사항은 주 메모리의 원래 값에 반영되지 않습니다. 다른 스레드가 수정된 값을 인식하지 못하기 때문에 데이터 불일치가 발생합니다.

 class Test { static volatile int var =5; } 

위의 예에서 정적 변수는 모든 객체 간에 공유되는 클래스 멤버입니다. 주 메모리에는 복사본이 하나만 있습니다. 휘발성 변수의 값은 캐시에 저장되지 않습니다. 모든 읽기 및 쓰기는 주 메모리에서 수행됩니다.

언제 사용하나요?

  • Long 변수와 Double 변수를 자동으로 읽고 쓰려면 휘발성 변수를 사용하면 됩니다.
  • 이는 Java에서 동기화를 달성하는 대체 방법으로 사용될 수 있습니다.
  • 모든 판독기 스레드는 쓰기 작업을 완료한 후 휘발성 변수의 업데이트된 값을 확인합니다. 휘발성 키워드를 사용하지 않는 경우 리더 스레드마다 다른 값이 표시될 수 있습니다.
  • 여러 스레드가 특정 명령문에 액세스할 것임을 컴파일러에 알리는 데 사용됩니다. 이는 컴파일러가 재정렬이나 최적화를 수행하는 것을 방지합니다.
  • 휘발성 변수를 사용하지 않는 경우 컴파일러는 코드를 재정렬할 수 있으며, 주 메모리에서 읽는 대신 휘발성 변수의 캐시 값을 자유롭게 쓸 수 있습니다.

중요사항

  • 변수와 함께 휘발성 키워드를 사용할 수 있습니다. 클래스 및 메소드와 함께 휘발성 키워드를 사용하는 것은 불법입니다.
  • 이는 휘발성 변수의 값이 항상 로컬 스레드 캐시가 아닌 주 메모리에서 읽혀지는 것을 보장합니다.
  • 변수를 휘발성으로 선언한 경우 읽기 및 쓰기는 원자성입니다.
  • 메모리 일관성 오류의 위험을 줄입니다.
  • Java에서 휘발성 변수에 대한 모든 쓰기는 동일한 변수의 연속적인 읽기와의 관계 이전에 발생합니다.
  • 휘발성 변수는 항상 다른 스레드에 표시됩니다.
  • 객체 참조인 휘발성 변수는 null일 수 있습니다.
  • 여러 스레드 간에 변수가 공유되지 않는 경우 해당 변수에 휘발성 키워드를 사용할 필요가 없습니다.

동기화와 휘발성 키워드의 차이점

휘발성 키워드는 동기화 키워드를 대체할 수는 없지만 경우에 따라 대안으로 사용할 수 있습니다. 다음과 같은 차이점이 있습니다.

휘발성 키워드 동기화 키워드
휘발성 키워드는 필드 수정자입니다. 동기화된 키워드는 코드 블록과 메소드를 수정합니다.
휘발성이 있는 경우 대기를 위해 스레드를 차단할 수 없습니다. 동기화된 경우 스레드가 대기하도록 차단될 수 있습니다.
스레드 성능이 향상됩니다. 동기화된 메서드는 스레드 성능을 저하시킵니다.
스레드 메모리와 메인 메모리 사이에서 한 번에 하나의 변수 값을 동기화합니다. 스레드 메모리와 메인 메모리 사이의 모든 변수의 값을 동기화합니다.
휘발성 필드는 컴파일러 최적화의 대상이 아닙니다. 동기화에는 컴파일러 최적화가 적용됩니다.

휘발성 키워드의 예

다음 예제에서는 카운터 값을 증가시키는 클래스를 정의했습니다. VolatileThread.java의 run() 메소드는 스레드가 실행을 시작할 때 업데이트된 값과 이전 값을 가져옵니다. 메인 클래스에서는 스레드 배열을 정의합니다.

VolatileData.java

 public class VolatileData { private volatile int counter = 0; public int getCounter() { return counter; } public void increaseCounter() { ++counter; //increases the value of counter by 1 } } 

VolatileThread.java

 VolatileThread.java public class VolatileThread extends Thread { private final VolatileData data; public VolatileThread(VolatileData data) { this.data = data; } @Override public void run() { int oldValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: Old value = ' + oldValue); data.increaseCounter(); int newValue = data.getCounter(); System.out.println('[Thread ' + Thread.currentThread().getId() + ']: New value = ' + newValue); } } 

휘발성Main.java

 public class VolatileMain { private final static int noOfThreads = 2; public static void main(String[] args) throws InterruptedException { VolatileData volatileData = new VolatileData(); //object of VolatileData class Thread[] threads = new Thread[noOfThreads]; //creating Thread array for(int i = 0; i <noofthreads; ++i) threads[i]="new" volatilethread(volatiledata); for(int i="0;" < noofthreads; threads[i].start(); starts all reader threads threads[i].join(); wait for } pre> <p> <strong>Output:</strong> </p> <pre> [Thread 9]: Old value = 0 [Thread 9]: New value = 1 [Thread 10]: Old value = 1 [Thread 10]: New value = 2 </pre> <hr></noofthreads;>