본문 바로가기
객체 지향 프로그래밍/디자인 패턴

[생성 패턴] 싱글톤 패턴 (Singleton Pattern) 이란?

by xangmin 2022. 5. 26.
반응형

싱글톤 패턴 이란?

어플리케이션이 시작될 때 어떤 클래스가 최초에 한 번만 메모리를 할당(static)하고 해당 메모리에 인스턴스를 만들어 사용

인스턴스가 필요할 때 똑같은 인스턴스를 만들지 않고 스턴스를 활용

생성자가 여러 번 호출되어도 실제 객체는 하나이며 최초 생성된 객체를 계속 반환

 

* 실제로 단 하나만 존재해야만 하는 물건이 있을 때 사용

 회사에서 단 하나의 프린터를 공유해서 쓰는 것과 유사하다.

 

싱글톤 패턴 장단점

장점

객체를 한 번만 생성하고 반환하기 때문에 메모리 영역을 한 번만 할당 (메모리 낭비 방지)

• 싱글톤으로 구현한 인스턴스는 전역이므로 다른 클래스의 인스턴스와 데이터 공유 가능

 

단점

싱글톤 인스턴스가 혼자 너무 많은 일을 하거나 공유하면 다른 클래스와의 결합도 증가

• 멀티 스레드 환경에서 동기화 처리를 하지 않으면 인스턴스가 1개 이상 생성

 

싱글톤 패턴 구현 방법

멀티 스레드 환경의 동시성 문제를 해결하기 위한 방법들이 존재한다.

 

Ex 1. 기본 사용법

public class Singleton {
    //싱글톤 객체를 static 변수로 선언
    private static Singleton instance;
    private int msg;
    
    //외부에서 생성자 호출 막기
    private Singleton(int msg) {
        this.msg = msg;
    }
    //인스턴스를 전달
    public static Singleton getInstance(int msg) {
        if (instance == null) {
            instance = new Singleton(msg);
        }
        return instance;
    } 
    
    public void printMsg() {
        System.out.println(msg);
    }
}
public class TestSingleton {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance(1);
        Singleton instance2 = Singleton.getInstance(2);
        instance.printMsg();
        instance2.printMsg();
    }
}

실행결과

1
1

 

Ex 2. 동시성 문제 (멀티 스레드 환경)

public class Singleton {
    private static Singleton instance;
    private int msg;

    private Singleton(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static Singleton getInstance(int msg) {
        if(instance == null) {
            instance = new Singleton(msg);
        }
        return instance;
    }
    public int getMsg() {
        return msg;
    }
}
public class TestSingleton {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            Singleton singleton = 
		Singleton.getInstance(num);
            System.out.println("instance : " + 
		singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

실행결과

//매번다름
6
5
4
8
2
11
3
7
9
10

 

Ex 3. Eager Initialization

public class Singleton {
    //선언과 동시에 초기화
    private static Singleton instance = new Singleton(0);
    private int msg;

    private Singleton(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static Singleton getInstance() {
        return instance;
    }
    public int getMsg() {
        return msg;
    }
}
public class TestSingleton {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            Singleton singleton = Singleton.getInstance();
            System.out.println("instance : " + singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

실행결과

0
0
0
0
0
0
0
0
0
0

 

Ex 4. Lazy Initialization (synchronized)

public class Singleton {
    private static Singleton instance;
    private int msg;
    private Singleton(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //synchronized 키워드 사용
    public static synchronized Singleton getInstance(int msg) {
        if(instance == null) {
            instance = new Singleton(msg);
        }
        return instance;
    }
    public int getMsg() {
        return msg;
    …
public class TestSingleton {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            Singleton singleton = 
		Singleton.getInstance(num);
            System.out.println("instance : " + 
		singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

실행결과

2
2
2
2
2
2
2
2
2
2

 

Ex 5. Lazy Initialization Double Checking Locking

public class Singleton {
    private static Singleton instance;
    private int msg;
    private Singleton(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static Singleton getInstance(int msg) {
    //instance가 null인 경우 synchronized 블록 접근
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(msg);
                }
            }
        }
        return instance;
    }
    public int getMsg() { …
public class TestSingleton {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            Singleton singleton = 
		Singleton.getInstance(num);
            System.out.println("instance : " + 
		singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

실행결과

2
2
2
2
2
2
2
2
2
2

 

Ex 6. Lazy Initialization (LazyHolder)

public class Singleton {
    private int msg;
    private Singleton(int msg) {
        try {
            Thread.sleep(100);
            this.msg = msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //static 클래스안에 static 멤버변수 선언/초기화
    private static class Initial {
        private static final Singleton instance = 
			new Singleton(0);
    }
    public static Singleton getInstance() {
        return Initial.instance;
    }
    public int getMsg() {
        return msg;
    }
}
public class TestSingleton {
    public static int num = 1;
    public static void main(String[] args) {
        Runnable run = () -> {
            num++;
            Singleton singleton = 
		Singleton.getInstance();
            System.out.println("instance : " + 
		singleton.getMsg());
        };
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(run);
            thread.start();
        }
    }
}

실행결과

0
0
0
0
0
0
0
0
0
0

 

반응형

댓글