[Java] Volatile 키워드란?

naneun·2022년 7월 14일
4

Java

목록 보기
7/7
post-thumbnail

  관련된 이전 포스트 의 내용을 간단히 요약하면

public class Singleton {

    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  해당 클래스는 여러 스레드에서 getInstance 메서드가 호출하면 Singleton 타입의 변수 공유 변수 (singleton) 에 대해 동시성 문제가 발생합니다.

  싱글톤 은 본래 객체가 하나만 생성되어 getInstance 메서드를 호출하는 모든 스레드에게 동일한 인스턴스 를 반환해야합니다. 하지만 현재의 getInstance 메서드나 공유 변수 (singleton) 는 어떠한 동기화 처리도 되어있지 않으며 이로인해 공유 변수가 null 일 때 여러 스레드가 getInstance 메서드를 동시에 호출하게 되면

if (singleton == null) {
	singleton = new Singleton();
}

  해당 if 문에 진입할 수 있으며 이로써 getInstance 를 호출한 스레드들은 각기 다른 인스턴스를 반환 받는 현상이 발생할 수 있습니다.

public static synchronized Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}

  getInstance 메서드에 synchronized 키워드를 붙여 메서드를 동기화 시켰고 (정적 메서드이므로 해당 인스턴스를 동기화하는 것이 아닌 클래스 레벨로 동기화합니다.)

public static Singleton getInstance() {
    synchronized (Singleton.class) {
        if (singleton == null) {
            singleton = new Singleton();
        }
    }
    return singleton;
}

  synchronized 위와 같이 synchronized 블록의 인자로 Singleton.class 를 주어 Singleton 클래스 자체를 동기화해 줄 수 있다.

Volatile 키워드란?

http://tutorials.jenkov.com/java-concurrency/volatile.html

  요약하자면 각 스레드는 메인 메모리 로부터 값을 복사해 CPU 캐시 에 저장하여 작업한다. CPU 가 2개 이상이라면 멀티 스레드 환경에서 각 스레드는 서로 다른 CPU 에서 동작하고 있으며 이는 각 스레드가 같은 변수에 대해 읽기, 쓰기 동작을 수행할 시 각자의 CPU 캐시메인 메모리 의 값과 다른 값을 갖고 있을 수 있게 된다.

  하지만 자바에서 어떠한 변수에 volatile 키워드를 붙이면 해당 변수는 모든 읽기와 쓰기 작업이 CPU 캐시가 아닌 메인 메모리 에서 이루어지게 되고 이로써 해당 변수 값에 대해 가시성 을 보장할 수 있다.

  사실 이 가시성 이란 용어는 읽기, 쓰기 작업에 대해 '동시성 문제 를 해결해준다' 는 의미가 아니다. 메인 메모리에서 작업이 이루어진다는 이유로 해당 키워드만으로 동기화 처리가 이루어진다고 잘못 받아들이면 다음과 같이

private static Singleton singleton;
private volatile Singleton singleton;

  단순히 static -> volatile 키워드만 변경하고 synchronized 키워드로 동기화 처리는 하지 않아도 되는 것으로 오해할 수 있다.

  하지만, 반드시 synchronized 키워드로 공유 객체에 대한 동기화 처리 까지 해주어야 싱글톤을 보장 받을 수 있다.

  1. volatile 키워드를 사용하여 모든 스레드가 항상 같은 공유 변수의 값을 읽어올 수 있도록 보장한 뒤
  1. 어느 한 스레드가 synchronized 키워드를 사용한 getInstance 메서드 (하나의 스레드만이 접근할 수 있는 메서드) 로 공유 변수에 인스턴스를 할당하면
  1. volatile 키워드에 의해 메인 메모리 에 해당 인스턴스의 값이 갱신되고 이로써 다른 모든 스레드들은 null 이 아닌 공유 변수에 할당된 인스턴스를 메인 메모리로부터 바로 읽어 올 수 있게 된다.
  1. 공유 변수 값의 불일치가 일어나지 않기 때문에 다른 스레드가 또 다시 getInstance 의 if 문 블록에 진입하는 경우는 발생하지 않게 된다.

성능 개선

  마지막으로 다음과 같이 작성한 getInstance 메서드는 항상 하나의 스레드만 접근 할 수 있어서 성능상의 문제가 있다.

public static Singleton getInstance() {
    synchronized (Singleton.class) {
        if (singleton == null) {
            singleton = new Singleton();
        }
    }
    return singleton;
}

  synchronized 블록 내의 아래 조건식은 인스턴스를 최초로 할당하는 경우에만 필요한 로직이다.

if (singleton == null) {
    singleton = new Singleton();
}

  따라서, 다음과 같이 if 조건문으로 한 번 더 감싸줌으로써 인스턴스가 할당된 뒤에 다수의 스레드에서 해당 인스턴스를 동시에 참조 할 수 있도록 변경해주었다.

 public static Singleton getInstance() {
     if (singleton == null) {
         synchronized (Singleton.class) {
             if (singleton == null) {
                 singleton = new Singleton();
             }
         }
     }
     return singleton;
 }
profile
riako

0개의 댓글