![싱글턴](/images/patterns/cards/singleton-mini.png?id=914e1565dfdf15f240e766163bd303ec)
자바로 작성된 싱글턴
싱글턴은 같은 종류의 객체가 하나만 존재하도록 하고 다른 코드의 해당 객체에 대한 단일 접근 지점을 제공하는 생성 디자인 패턴입니다.
싱글턴은 전역 변수들과 거의 같은 장단점을 가지고 있습니다: 매우 편리하나 코드의 모듈성을 깨뜨립니다.
싱글턴에 의존하는 클래스를 다른 콘텍스트에서 사용하려면 싱글턴도 다른 콘텍스트로 전달해야 합니다. 대부분의 경우 이 제한 사항은 유닛 테스트를 생성하는 동안 발생합니다.
내비게이션
복잡도:
인기도:
사용 사례들: 많은 개발자는 싱클턴을 안티패턴으로 간주합니다. 그래서 자바 코드에서의 사용이 감소하고 있습니다.
그럼에도 불구하고 자바 코어 라이브러리에는 많은 싱글턴 사용사례들이 존재합니다:
식별: 싱글턴은 같은 캐싱 된 객체를 반환하는 정적 생성 메서드로 식별될 수 있습니다.
기본 싱글턴 (단일 스레드)
조잡한 싱글턴을 구현하는 것은 매우 쉽습니다. 생성자를 숨기고 정적 생성 메서드를 구현하기만 하면 됩니다.
Singleton.java: 싱글턴
package refactoring_guru.singleton.example.non_thread_safe;
public final class Singleton {
private static Singleton instance;
public String value;
private Singleton(String value) {
// The following code emulates slow initialization.
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
this.value = value;
}
public static Singleton getInstance(String value) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
DemoSingleThread.java: 클라이언트 코드
package refactoring_guru.singleton.example.non_thread_safe;
public class DemoSingleThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Singleton singleton = Singleton.getInstance("FOO");
Singleton anotherSingleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
System.out.println(anotherSingleton.value);
}
}
OutputDemoSingleThread.txt: 실행 결과
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
FOO
기본 싱글턴 (멀티스레드)
같은 클래스는 다중 스레드 환경에서 잘못 작동합니다. 여러 스레드가 생성 메서드를 동시에 호출할 수 있으며 싱글턴 클래스의 여러 인스턴스를 가져올 수 있기 때문입니다.
Singleton.java: 싱글턴
package refactoring_guru.singleton.example.non_thread_safe;
public final class Singleton {
private static Singleton instance;
public String value;
private Singleton(String value) {
// The following code emulates slow initialization.
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
this.value = value;
}
public static Singleton getInstance(String value) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
DemoMultiThread.java: 클라이언트 코드
package refactoring_guru.singleton.example.non_thread_safe;
public class DemoMultiThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Thread threadFoo = new Thread(new ThreadFoo());
Thread threadBar = new Thread(new ThreadBar());
threadFoo.start();
threadBar.start();
}
static class ThreadFoo implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("FOO");
System.out.println(singleton.value);
}
}
static class ThreadBar implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
}
}
}
OutputDemoMultiThread.txt: 실행 결과
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
BAR
지연 로딩이 있는 스레드로부터 안전한 싱글턴
이 문제를 해결하려면 싱글턴 객체를 처음 생성하는 동안 스레드들을 동기화해야 합니다.
Singleton.java: 싱글턴
package refactoring_guru.singleton.example.thread_safe;
public final class Singleton {
// The field must be declared volatile so that double check lock would work
// correctly.
private static volatile Singleton instance;
public String value;
private Singleton(String value) {
this.value = value;
}
public static Singleton getInstance(String value) {
// The approach taken here is called double-checked locking (DCL). It
// exists to prevent race condition between multiple threads that may
// attempt to get singleton instance at the same time, creating separate
// instances as a result.
//
// It may seem that having the `result` variable here is completely
// pointless. There is, however, a very important caveat when
// implementing double-checked locking in Java, which is solved by
// introducing this local variable.
//
// You can read more info DCL issues in Java here:
// https://refactoring.guru/java-dcl-issue
Singleton result = instance;
if (result != null) {
return result;
}
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton(value);
}
return instance;
}
}
}
DemoMultiThread.java: 클라이언트 코드
package refactoring_guru.singleton.example.thread_safe;
public class DemoMultiThread {
public static void main(String[] args) {
System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
"If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
"RESULT:" + "\n");
Thread threadFoo = new Thread(new ThreadFoo());
Thread threadBar = new Thread(new ThreadBar());
threadFoo.start();
threadBar.start();
}
static class ThreadFoo implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("FOO");
System.out.println(singleton.value);
}
}
static class ThreadBar implements Runnable {
@Override
public void run() {
Singleton singleton = Singleton.getInstance("BAR");
System.out.println(singleton.value);
}
}
}
OutputDemoMultiThread.txt: 실행 결과
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
BAR
BAR
더 많은 정보를 원하시나요?
자바에는 싱글턴 패턴의 더 많은 변형이 있습니다. 자세한 내용을 알아보시려면 이 기사를 참조하세요: