Posted:      Updated:

자바 디자인 패턴 스터디를 하며 ‘Java 객체 지향 디자인 패턴’ 교재를 정리한 글입니다.

싱글턴 패턴(Singleton Pattern)

  • 인스턴스가 오직 하나만 생성되는 것을 보장
  • 어디서든 이 인스턴스에 접근 가능
  • ‘단 하나의 원소만을 가진 집합’ 이라는 수학 이론에서 유래
  • 클라이언트가 싱글턴 클래스에 getInstance 메서드를 통해 객체 생성을 요청
  • 이미 객체가 생성된 경우 객체를 반환
  • 처음 생성하는 경우 생성자를 호출해 객체 생성

예시: 프린터 관리자 만들기

  • Printer 클래스를 사용한 프로그램
  • Printer 는 반드시 하나

단일 스레드 환경에서의 싱글턴

구현 방법

  • 외부에서 생성자를 호출할 수 없음
    • 생성자를 private 로 선언
    • 인스턴스를 만들어 제공하는 메서드 생성
  • 인스턴스가 이미 생성되어 있는지 검사
    • 생성되지 않은 상황이면 생성자 호출해 인스턴스 생성
    • 이미 생성되었다면 참조하는 인스턴스 반환
  • printer 변수, getPrinter 메서드는 static 타입으로 선언

전체 코드

  • 5명의 사용자가 프린터를 이용하는 상황
public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public void print() {
        Printer printer = Printer.getPrinter();
        printer.print(this.name + " print using " + printer.toString() + ".");
    }
}

public class Printer {
    private static Printer printer = null;
    private Printer() { }

    public static Printer getPrinter() {
        if (printer == null) {
            printer = new Printer();
        }
        return printer;
    }

    public void print(String str) {
        System.out.println(str);
    }
}

public class Main {
    private static final int User_NUM = 5;

    public static void main(String[] args) {
        User[] user = new User[User_NUM];
        for (int i = 0; i < User_NUM; i++) {
            user[i] = new User((i + 1) + "-user");
            user[i].print();
        }
    }
}

출력

1-user print using Printer@6aaa5eb0.
2-user print using Printer@6aaa5eb0.
3-user print using Printer@6aaa5eb0.
4-user print using Printer@6aaa5eb0.
5-user print using Printer@6aaa5eb0.

다중 스레드 환경에서의 싱글턴

정적 변수에 인스턴스를 만들어 바로 초기화

  • 클래스가 메모리에 로딩될 때 만들어져 초기화 한 번만 실행
  • 정적 메서드 getPrinter 를 통해 참조되는 인스턴스를 얻을 수 있음
Printer 클래스 코드
public class Printer {
    private static Printer printer = new Printer();
    private int counter = 0;
    private Printer() { }

    public static Printer getPrinter() {
        return printer;
    }

    public void print(String str) {
        counter++;
        System.out.println(str);
    }
}

getPrinter 메서드 동기화

  • synchronized 사용 👉 여러 스레드가 하나의 자원을 사용하고자 할 때, 현재 데이터를 활용하고 있는 스레드 외의 다른 스레드들은 데이터에 접근할 수 없도록 막음
Printer 클래스 코드
public class Printer {
    private static Printer printer = null;
    private int counter = 0;
    private Printer() { }

    public synchronized static Printer getPrinter() {
        if (printer == null) {
            printer = new Printer();
        }
        return printer;
    }

    public void print(String str) {
        synchronized (this) {
            counter++;
            System.out.println(str+counter);
        }
    }
}

정적 클래스

  • 정적 메서드로 이루어진 정적 클래스
  • 굳이 싱글톤 패턴을 사용하지 않아도 동일한 효과를 얻을 수 있음
  • 객체를 생성하지 않고 메서드를 사용
  • 인터페이스에서는 사용할 수 없음

정적 클래스로 구현한 예제

  • 객체를 전혀 생성하지 않으며 메서드를 사용함
  • 정적 메서드를 사용하여 인스턴스 메서드를 사용하는 것보다 성능 면에서 우수

전체 코드

public class Printer {
    private static int counter = 0;

    public synchronized static void print(String str) {
        counter++;
        System.out.println(str + counter);
    }
}

public class UserThread extends Thread{
    public UserThread(String name) {
        super(name);
    }

    public void run() {
        Printer.print(Thread.currentThread().getName()+" print using .");
    }
}

public class Main {
    private static final int THREAD_NUM = 5;
    public static void main(String[] args) {
        UserThread[] user = new UserThread[THREAD_NUM];

        for (int i = 0; i < THREAD_NUM; i++) {
            user[i] = new UserThread((i+1)+"-thread");
            user[i].start();
        }
    }
}

출력

2-thread print using .1
1-thread print using .2
4-thread print using .3
3-thread print using .4
5-thread print using .5

댓글남기기