Java Language
싱글 톤
수색…
소개
싱글 톤은 하나의 인스턴스 만 갖는 클래스입니다. Singleton 디자인 패턴 에 대한 자세한 내용은 Design Patterns 태그의 Singleton 항목을 참조하십시오.
열거 형 싱글 톤
public enum Singleton {
INSTANCE;
public void execute (String arg) {
// Perform operation here
}
}
Enum 에는 개인 생성자가 있고 최종적으로 적절한 직렬화 기계를 제공합니다. 그들은 또한 매우 간결하고 게으른 스레드 안전 방식으로 초기화됩니다.
JVM은 열거 형 값이 각각 두 번 이상 인스턴스화되지 않는다는 보장을 제공하여 열거 형 싱글 톤 패턴이 반사 공격에 대해 매우 강력한 방어력을 제공합니다.
enum 패턴 이 보호 하지 못하는 것은 다른 개발자가 실제로 소스 코드에 더 많은 요소를 추가하는 것입니다. 따라서 싱글 톤에 대해이 구현 스타일을 선택하는 경우 해당 열거 형에 새 값을 추가하지 말 것을 매우 분명하게 문서화해야합니다.
Joshua Bloch가 Effective Java에서 설명한 것처럼 싱글 톤 패턴을 구현하는 데 권장되는 방법입니다.
이중 안전 잠금이있는 스레드 안전 싱글 톤
이 유형의 Singleton은 스레드로부터 안전하며 Singleton 인스턴스가 생성 된 후 불필요한 잠금을 방지합니다.
public class MySingleton {
// instance of class
private static volatile MySingleton instance = null;
// Private constructor
private MySingleton() {
// Some code for constructing object
}
public static MySingleton getInstance() {
MySingleton result = instance;
//If the instance already exists, no locking is necessary
if(result == null) {
//The singleton instance doesn't exist, lock and check again
synchronized(MySingleton.class) {
result = instance;
if(result == null) {
instance = result = new MySingleton();
}
}
}
return result;
}
}
자바 SE 5 이전 버전에서는 위의 구현이 잘못 되었으므로이를 피해야합니다. Java 5 이전에는 Java에서 이중 검사 잠금을 올바르게 구현할 수 없습니다.
열거 형을 사용하지 않는 싱글 톤 (열렬한 초기화)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
이 예제는 실제로 게으른 초기화라고 주장 할 수 있습니다. Java 언어 사양의 섹션 12.4.1에서는,
클래스 또는 인터페이스 타입 T는, 다음 중 어느 것이 최초로 발생하기 직전에 초기화됩니다.
- T는 클래스이고 T의 인스턴스가 생성됩니다.
- T는 클래스이고 T에 의해 선언 된 정적 메서드가 호출됩니다.
- T로 선언 된 정적 필드가 할당됩니다.
- T로 선언 된 정적 필드가 사용되고 필드는 상수 변수가 아닙니다.
- T는 최상위 클래스이며 T 내에 어휘 적으로 중첩 된 assert 문이 실행됩니다.
따라서 클래스에 다른 정적 필드 나 정적 메서드가 없으면 getInstance()
메서드가 처음 호출 될 때까지 Singleton
인스턴스가 초기화되지 않습니다.
홀더 클래스를 사용한 thread-safe lazy 초기화 | Bill Pugh Singleton 구현
public class Singleton {
private static class InstanceHolder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return InstanceHolder.INSTANCE;
}
private Singleton() {}
}
이것은 추가적인 동기화없이 정적 초기화에 대한 언어의 스레드 안전 보장을 이용하여 Singleton.getInstance()
에 대한 첫 번째 호출에서 INSTANCE
변수를 초기화합니다.
이 구현은 Bill Pugh 싱글 톤 패턴이라고도합니다. [위키]
싱글 톤 확장 (싱글 톤 상속)
이 예제에서 기본 클래스 인 Singleton
은 "Hello world!"
를 반환하는 getMessage()
메서드를 제공합니다 "Hello world!"
메시지.
서브 클래스 UppercaseSingleton
과 LowercaseSingleton
은 메시지의 적절한 표현을 제공하기 위해 getMessage () 메소드를 대체합니다.
//Yeah, we'll need reflection to pull this off.
import java.lang.reflect.*;
/*
Enumeration that represents possible classes of singleton instance.
If unknown, we'll go with base class - Singleton.
*/
enum SingletonKind {
UNKNOWN,
LOWERCASE,
UPPERCASE
}
//Base class
class Singleton{
/*
Extended classes has to be private inner classes, to prevent extending them in
uncontrolled manner.
*/
private class UppercaseSingleton extends Singleton {
private UppercaseSingleton(){
super();
}
@Override
public String getMessage() {
return super.getMessage().toUpperCase();
}
}
//Another extended class.
private class LowercaseSingleton extends Singleton
{
private LowercaseSingleton(){
super();
}
@Override
public String getMessage() {
return super.getMessage().toLowerCase();
}
}
//Applying Singleton pattern
private static SingletonKind kind = SingletonKind.UNKNOWN;
private static Singleton instance;
/*
By using this method prior to getInstance() method, you effectively change the
type of singleton instance to be created.
*/
public static void setKind(SingletonKind kind) {
Singleton.kind = kind;
}
/*
If needed, getInstance() creates instance appropriate class, based on value of
singletonKind field.
*/
public static Singleton getInstance()
throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
Singleton singleton = new Singleton();
switch (kind){
case UNKNOWN:
instance = singleton;
break;
case LOWERCASE:
/*
I can't use simple
instance = new LowercaseSingleton();
because java compiler won't allow me to use
constructor of inner class in static context,
so I use reflection API instead.
To be able to access inner class by reflection API,
I have to create instance of outer class first.
Therefore, in this implementation, Singleton cannot be
abstract class.
*/
//Get the constructor of inner class.
Constructor<LowercaseSingleton> lcConstructor =
LowercaseSingleton.class.getDeclaredConstructor(Singleton.class);
//The constructor is private, so I have to make it accessible.
lcConstructor.setAccessible(true);
// Use the constructor to create instance.
instance = lcConstructor.newInstance(singleton);
break;
case UPPERCASE:
//Same goes here, just with different type
Constructor<UppercaseSingleton> ucConstructor =
UppercaseSingleton.class.getDeclaredConstructor(Singleton.class);
ucConstructor.setAccessible(true);
instance = ucConstructor.newInstance(singleton);
}
}
}
}
return instance;
}
//Singletons state that is to be used by subclasses
protected String message;
//Private constructor prevents external instantiation.
private Singleton()
{
message = "Hello world!";
}
//Singleton's API. Implementation can be overwritten by subclasses.
public String getMessage() {
return message;
}
}
//Just a small test program
public class ExtendingSingletonExample {
public static void main(String args[]){
//just uncomment one of following lines to change singleton class
//Singleton.setKind(SingletonKind.UPPERCASE);
//Singleton.setKind(SingletonKind.LOWERCASE);
Singleton singleton = null;
try {
singleton = Singleton.getInstance();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
System.out.println(singleton.getMessage());
}
}