Java 싱글톤(Singleton) 패턴 : 객체를 하나만 생성하고, 그 객체를 어디서든 접근할 수 있도록 하는 디자인 패턴입니다. 이 패턴은 특정 클래스의 인스턴스가 하나만 생성되도록 보장하며, 이 인스턴스에 대한 전역적인 접근점을 제공합니다.

 

 

  • 싱글턴 패턴을 사용하지 않으면:
    1. 매번 새 인스턴스가 생성됩니다.
    2. 클래스의 생성자가 매번 호출되어 새로운 객체가 생성됩니다.

 

public class MyClass {
    public MyClass() {
        System.out.println("MyClass constructor called!");
    }

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        MyClass obj1 = new MyClass();  // 첫 번째 인스턴스 생성
        obj1.doSomething();

        MyClass obj2 = new MyClass();  // 두 번째 인스턴스 생성
        obj2.doSomething();
    }
}
  • 출력결과  : 
MyClass constructor called! //생성자 1회 호출
Doing something...
MyClass constructor called! //생성자 2회 호출
Doing something...

  • 싱글톤 패턴의 주요 특징 :
    1. 유일한 인스턴스: 클래스의 인스턴스가 하나만 존재하도록 보장합니다.
    2. 전역 접근: 애플리케이션 어디에서든지 이 인스턴스에 접근할 수 있도록 합니다.
    3. 인스턴스 제어: 인스턴스 생성이 제한되므로 불필요한 리소스 사용을 방지할 수 있습니다.

 

  • 싱글톤 예제 1번  :  Double-Checked Locking 방식  성능 최적화를 위해 흔히 사용하는 방법 중 하나는 Double-Checked Locking입니다. 이 방법에서는 인스턴스가 이미 생성된 후에는 동기화를 피하는 방식으로, 성능을 최적화할 수 있습니다.
public class Singleton {
    private static volatile Singleton instance;
    private final String name;
    private final int age;

    // private 생성자, 값을 초기화하는 역할
    private Singleton(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getInstance 메서드는 인스턴스를 한 번만 초기화하고 이후에는 같은 인스턴스를 반환
    public static Singleton getInstance(String name, int age) {
        if (instance == null) {  // 첫 번째 체크 (동기화 없이)
            synchronized (Singleton.class) {
                if (instance == null) {  // 두 번째 체크 (동기화된 상태에서)
                    // 첫 번째 인스턴스 생성 시 name과 age를 설정
                    instance = new Singleton(name, age);
                }
            }
        }
        return instance;
    }

    // Getter 메서드
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // 이 메서드는 인스턴스의 값을 변경할 수 없게 만들어서 불변성 유지
    public static void resetInstance(String name, int age) {
        throw new UnsupportedOperationException("Singleton instance cannot be reset");
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        // 첫 번째 인스턴스 생성 (name과 age 값 설정)
        Singleton singleton = Singleton.getInstance("John", 30);
        System.out.println("Name: " + singleton.getName());
        System.out.println("Age: " + singleton.getAge());

        // 동일한 인스턴스를 반환, 다른 값으로 초기화 불가능
        Singleton singleton2 = Singleton.getInstance("Jane", 25);
        System.out.println("Name: " + singleton2.getName()); // 기존 값 출력 ("John")
        System.out.println("Age: " + singleton2.getAge());   // 기존 값 출력 (30)

        // resetInstance() 메서드를 호출하여 재설정 불가
        // singleton.resetInstance("New Name", 40); // 실행 시 예외 발생
    }
}

 

  • 싱글턴 클래스 요소 :
    1. 단일 인스턴스 보장: getInstance 메서드는 항상 동일한 인스턴스를 반환합니다. instance가 null인 경우에만 새로운 인스턴스를 생성하고, 그 후에는 기존의 인스턴스를 반환합니다.
    2. 멀티스레드 안전성: synchronized 키워드를 사용하여 멀티스레드 환경에서 동기화를 처리하고, 두 번째 체크를 통해 불필요한 동기화 작업을 피하고 성능을 최적화합니다.
    3. 불변 객체: name과 age 값은 객체가 한 번 생성되면 변경할 수 없습니다. 이는 객체의 불변성을 보장하는 중요한 요소입니다.

 

  • 싱글톤 예제 2번  :    
    • enum은 자바에서 기본적으로 Serializability, Thread-Safety, Instance Control을 보장해주기 때문에, 이를 활용하여 Singleton 패턴을 구현하면 추가적인 코드 없이 간단하게 안전한 싱글턴 객체를 생성할 수 있습니다. enum 사용한 싱글턴 패턴은 구현이 간단하면서도, 멀티스레드 환경과 직렬화 문제를 자연스럽게 해결하는 강력한 방법입니다. 따라서 enum을 이용한 싱글턴 구현은 가장 안전하고 바람직한 방식입니다.
 enum Singleton {
    UNIQUE_INSTANCE("John", 30);  // name과 age 값을 전달

    private final String name;
    private final int age;

    // 생성자
    Singleton(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Singleton instance created!");
    }

    // 싱글턴 객체에서 사용할 메소드들
    public void doSomething() {
        System.out.println("Doing something...");
    }

    // 값들을 반환하는 메소드
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton single = Singleton.UNIQUE_INSTANCE;
        single.doSomething();  // 첫 번째 호출
        System.out.println("Name: " + single.getName() + ", Age: " + single.getAge());

        Singleton single2 = Singleton.UNIQUE_INSTANCE;
        single2.doSomething(); // 두 번째 호출
        System.out.println("Name: " + single2.getName() + ", Age: " + single2.getAge());
    }
}
  • 출력결과  : 
Singleton instance created! //생성자 1번만 호출 
Doing something...
Name: John, Age: 30
Doing something...
Name: John, Age: 30

 

+ Recent posts