Software/Programming2016. 4. 15. 04:19

응용. singleton 패턴과 volatile---DCL(Double Checking Locking)


간단한 싱글톤 예제를 작성하겠다


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static synchronized Singleton getInstance(){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

}


위 타입의 인스턴스는 유일하게 하나만 존재해야 한다. 그러나 synchronized가 없을 경우, 다수의 쓰레드에서 인스턴스 생성/접근을 시도했을 때, 다수의 인스턴스가 발생할 가능성이 있다.

ex)

1. Thread1 에서 if(uniqueInstance == null) 에 접근

2. Thread1 이 다은 행을 이행하기 전에 Thread2에서 if(uniqueInstance == null) 실행

3. Thread1, 2에서 각각 new Singleton() 실행


synchronized를 적용하면 한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 한다.

그러나 이 경우 접근에 너무 제한이 들어 동시에 접근이 일어났을 때 처리가 늦어진다.(비용이 너무 크다)

따럿 아래와 같은 double checking locking 기법을 사용한다.


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}


설사 Thread2가 객체생성 전에 첫번째 checking에 진입하여도 synchronized에 걸리게 된다.

이미 객체가 생성되어있을 경우 synchronized에 걸릴것 없이 바로 객체를 받게된다.


그러나 아래와 같은 low-level 문제가 발생할 수 있다.


public class Singleton{

private static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

some_memory_space = aloocate space for Singleton object;

uniqueInstance = some_memory_space;

create a real object in some_memory_space;

}

}

}

return uniqueInstance;

}

}


new를 사용하여 객체를 생성할 때, 메모리 공간의 확보-변수에 메모리 공간 링크-해당 메모리에 오브젝트 생성 순으로 작업이 이루어진다. 이 때 아래와 같은 문제가 발생할 수 있다.


1. Thread1은 '변수에 메모리 공간 링크'만 된 상태

2. Thread2가 첫번째 if문에 걸림. 오브젝트가 생성되었다고 착각

3. Thread2가 getInstance()함수를 탈출, 해당 메모리 공간(오브젝트가 아직 미생성) 으로 작업을 하려고 함.

4. 오브젝트가 생성되지 않았기 때문에 에러 발생

이것이 재배치(reordering)과정에 의해 발생하는 문제이다.


public class Singleton{

private volatile static Singleton uniqueInstance;


private Singleton() {}


public static Singleton getInstance(){

if(uniqueInstance == null){

synchronized (Singleton.class){

if(uniqueInstance == null){

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}


volatile이 적용된 코드이다. volatile을 적용시켰기 때문에 오브젝트 생성/메인메모리에 배치까지 바로 업데이트가 되어 재배치 문제가 해소된다.


그러나 1.5 미만의 버전의 경우, 변수 자체(uniqueInstance)에만 업데이트 해주도록 되어있다. uniqueInstance = some_memory_space라는 대입과정이 메인 메모리에 실행만 하면 되기 때문에 오브젝트 생성전에 앞선 문제가 발생할 수 있다.


1.5 이상의 버전의 경우 모든 값(상태)가 업데이트가 된다. 즉, 재배치를 스킵하고 오브젝트 생성까지 진행되고 메인 메모리에 업데이트가 되기 때문에 문제가 발생할 소지가 없다는 것이다.

Posted by 십자성군