Hello, Freakin world!

[Java] volatile에 대한 고찰(용도, 스레드 세이프, 실제적 쓰임) 본문

프로그래밍 언어/Java

[Java] volatile에 대한 고찰(용도, 스레드 세이프, 실제적 쓰임)

johnna_endure 2020. 4. 2. 20:24

용도는?

 

출처 : https://3.bp.blogspot.com/-o2VBopq_Bcc/VKAXLDExe9I/AAAAAAAACTM/KtLZB1WVBGk/s640/volatile%2Bvariable%2Bin%2BJava.png

 

volatile의 용도는 다중 스레드 프로그래밍에서 변수 메모리의 위치를 스레드의 로컬(위의 Working Memory)이 아니라 Main Memory에 생성함으로서 일관성있는 데이터의 참조가 가능한 변수를 만들기 위함입니다.

 

하지만 과연 스레드 세이프할까요?

 

스레드 세이프 한가?

만약 volatile을 적용한 변수에 대해서 읽고 쓰기만 한다면 스레드 세이프합니다.

 

하지만 volatile변수의 연산 결과를 로컬 변수에 저장하고, 다시 이 로컬 변수를 volatile변수에 할당하는 경우에 

데이터의 일관성이 깨질 수 있습니다.

 volatile int n = 1;
 public void modify() {
    int temp = n + 1;
    n = temp;
 }

위의 경우에서 n+1 연산을 하는 도중 다른 스레드에서 n 변수를 읽는다면 1을 가집니다. 

이렇게 연산이 atomic 하지 않다면(연산이 둘 이상의 단계를 거친다면) 그 시간 차로 인해서 데이터의 일관성이 깨질 수 있습니다. 

 

만약 값을 여러 단계에 걸쳐서 변경해야할 필요가 있다면, 로컬 변수를 사용하면 안됩니다. 

모든 단계에서 volatile 변수에 직접 값을 할당해야 합니다. 

 

volatile 변수를 스레드 내에서 변경해야할 필요가 있다면, volatile보단 자바에서 지원하는 concurrent.atomic 패키지의 클래스들을 살펴보세요. 

 

 

그럼 쓸데가 없는거 아니냐?

아닙니다. 

단순 플래그나 객체의 참조는 사용하는 목적에 따라서 volatile 변수를 유용하게 활용할 수 있습니다.

 

스레드에서 volatile 변수를 플래그로 사용하는 한 가지 예입니다.

public class WorkThread extends Thread{
    volatile boolean runningFlag = true;

    @Override
    public void run() {
        while(runningFlag) {
            System.out.println("스레드 돌아가는 중");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void stopThread() {
        runningFlag = false;
    }
}

 

자바에서 stop 메서드가 안정성의 이유로 폐기되면서, 개발자는 스레드를 안전하게 종료하는 방법을 제공해야합니다.

위는 스레드를 종료하는 하나의 방법입니다. (이것도 조만간 블로그에 정리해서 올릴 예정입니다.)

 

위에서 volatile 변수를 사용한 이유는 stopThread의 요청을 신속하게 전달하기 위해서입니다.

만약 volatile을 사용하지 않았을 경우,  stopThead 메서드와 while(runningFlag) 에서 변수에 대해 동시에 접근한다고 가정해보세요. 어떤 경우엔, stopThread를 호출했음에도 바로 정지하지 않고 한번 더 루프를 돈 후에 정지될겁니다.

 

이처럼 atomic한 read/write 작업에는 충분히 volatile변수는 권장될 수 있습니다.

Comments