클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공
초콜릿 공장에서 초콜릿을 끓이는 장치를 컴퓨터로 제어한다.
보일러는 초클릿이 비어있을때만 돌아가고 비어있으면 초콜릿을 넣는다. 오류가 생길시 500갤런의 초콜릿을 그냥 흘러보낼 수 있다.
public class ChocolateBoiler {
private boolean empty;
private boolean boiled;
private ChocolateBoiler(){
empty = true;
boiled = false;
}
public void fill() {
if (empty){
empty = false;
boiled = false;
// 보일러에 우유와 초콜릿을 혼합한 재료를 넣음
}
}
public void drain() {
if (!empty && boiled) {
empty = true;
}
}
public void boil() {
if (!empty && !boiled) {
}
}
}
최근 반영한 코드에서 멀티 스레드를 사용하도록 초콜릿 보일러 컨트롤러를 최적화 시켰는데 500갤런이나 되는 우유와 초콜릿이 흘러넘쳤다.
public class Singleton {
private static Singleton singleton;
// 기타 변수
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// 기타 메소드
}
하지만 getInstance()의 속도가 중요하다면 문제가 병목을 야기할 수 있다.
(동기화 선언하면 성능이 100배 정도 저하된다.)
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
}
위의 동기화 속도 저하 문제는 똑같이 해결되지 않는다.
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();
}
}
}
}
}
DCL(Double-Checked Locking)
을 사용하면 인스터스가 생성되어 있는지 확인한 다음 생성되어 있지 않았을 때만 동기화 가능(처음만 동기화)
그렇다면 가장 효율적인 DCL을 적용해서 초기 초콜릿 보일러 코드를 변경한다면
public class ChocolateBoiler {
// 인스턴스 변수
private static ChocolateBoiler uniqueInstance;
private ChocolateBoiler(){
empty = true;
boiled = false;
}
public static ChocolateBoiler getInstance(){
if (uniqueInstance == null) {
synchronized (ChocolateBoiler.class) { // 클래스 수준 동기화 ChocolateBoiler 클래스에 모니터 락
if (uniqueInstance == null) {
uniqueInstance = new ChocolateBoiler();
}
}
}
return uniqueInstance;
}
// 인스턴스 메소드
}
synchronized (ChocolateBoiler.class)
- 인스턴스 메소드 : 객체가 있어야만 호출할 수 있는 메소드
boiler.boil();
- 클래스 메소드 (static 메소드) : 클래스 자체에 종속된 메소드 -> 객체가 없어도 호출할 수 있는 메소드
Math.random()
synchornized (this)
처럼 객체 수준에서 동기화를 걸면, 해당 객체만 동기화public synchronized void methodA(){
// 해당 인스턴스 에만 동기화
}
synchronized (ChocolateBoiler.class)
처럼 클래스 자체에 동기화를 걸면, 해당 클래스에서 만들어진 모든 객체에 대해 동기화 적용public static synchronized void staticMethod(){
// 모든 ChocolateBoiler 객체에서 동기화
}