[๋””์ž์ธํŒจํ„ด] ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด (Singleton Pattern)

๋ฐ˜์‘ํ˜•
๐Ÿ’ก ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด (singleton pattern)
ํ•ด๋‹น ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด์ง€๊ณ , ์–ด๋””์„œ๋“ ์ง€ ๊ทธ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ์œ„ํ•œ ํŒจํ„ด

 

ํด๋ž˜์Šค์—์„œ ์ž์‹ ์˜ ๋‹จ ํ•˜๋‚˜๋ฟ์ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

๋‹ค๋ฅธ ์–ด๋–ค ํด๋ž˜์Šค์—์„œ๋„ ์ž์‹ ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ถ”๊ฐ€๋กœ ๋งŒ๋“ค์ง€ ๋ชปํ•˜๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

โ–  ๊ณ ์ „์ ์ธ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ๊ตฌํ˜„

public class Singleton {

    private static Singleton uniqueInstance;

    // ์ƒ์„ฑ์ž : ์™ธ๋ถ€์—์„œ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ•˜๋„๋ก private
    private Singleton(){}

    // ์ƒ์„ฑ์ž ๋Œ€์‹  ์ƒ์„ฑํ• ๋•Œ ์‚ฌ์šฉํ•  getInstance() ๋ฉ”์†Œ๋“œ
    public static Singleton getInstance(){
        if (uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

}

uniqueInstance๋ฅผ null ์„ ์ฒดํฌํ•˜์—ฌ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์œ ์ผํ•œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ €์žฅ๋œ๋‹ค. ํ•„์š”ํ•œ ์ƒํ™ฉ์ด ๋˜๊ธฐ ์ „๊นŒ์ง€ ์ƒ์„ฑํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฒŒ์œผ๋ฅธ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (lazy instantiation) ์ด๋ผ ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ƒ์„ฑํ•˜๊ณ  ๊ทธ๋ƒฅ ๋ด์„œ๋Š” ๋ณ„๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ์—†์–ด๋ณด์ธ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์—†์„๊นŒ?

๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ์ผ ๋•Œ,  2๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ์—์„œ Singleton.getInstance() ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

 

๋ฌธ์ œ์  : ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์ทจ์•ฝ

// 1๋ฒˆ ์“ฐ๋ ˆ๋“œ
public static Singleton getInstance(){ // -> uniqueInstance : null ์ด๋‹ค
// 2๋ฒˆ ์“ฐ๋ ˆ๋“œ๋กœ ์ œ์–ด๊ถŒ ๋„˜์–ด๊ฐ
public static Singleton getInstance(){ // -> uniqueInstance : null ์ด๋‹ค
// 1๋ฒˆ ์“ฐ๋ ˆ๋“œ๋กœ ์ œ์–ด๊ถŒ ๋„˜์–ด๊ฐ
if (uniqueInstance == null) { // ์กฐ๊ฑด ๋งŒ์กฑ
// 2๋ฒˆ ์“ฐ๋ ˆ๋“œ๋กœ ์ œ์–ด๊ถŒ ๋„˜์–ด๊ฐ
if (uniqueInstance == null) { // ์กฐ๊ฑด ๋งŒ์กฑ
// 1๋ฒˆ ์“ฐ๋ ˆ๋“œ๋กœ ์ œ์–ด๊ถŒ ๋„˜์–ด๊ฐ
uniqueInstance = new Singleton(); // uniqueInstance : Object1 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
return uniqueInstance // Object1 ์ธ์Šคํ„ด์Šค ๋ฆฌํ„ด
// 2๋ฒˆ ์“ฐ๋ ˆ๋“œ๋กœ ์ œ์–ด๊ถŒ ๋„˜์–ด๊ฐ
uniqueInstance = new Singleton(); // uniqueInstance : Object2 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
return uniqueInstance // Object2 ์ธ์Šคํ„ด์Šค ๋ฆฌํ„ดโ€‹

๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด๋Ÿฐ ๋™์ž‘์œผ๋กœ ์ธํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ๊ฐœ์˜ ๊ฐ์ฒด (Object1, Object2) ๊ฐ€ ์ƒ์„ฑ๋˜์–ด ๋ฒ„๋ฆฐ๋‹ค.

์ฆ‰, ์“ฐ๋ ˆ๋“œ ์„ธ์ดํ”„ (thread safety) ํ•˜์ง€ ์•Š๋‹ค.

 

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ์—†์„๊นŒ? ์“ฐ๋ ˆ๋“œ ์„ธ์ดํ”„ ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—†์„๊นŒ?

โ–  ๋™๊ธฐํ™” (synchronized) ํ‚ค์›Œ๋“œ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•

getInstance()๋ฅผ ๋™๊ธฐํ™”์‹œ์ผœ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋ฌธ์ œ๋ฅผ ๊ฐ„๋‹จํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton() {}

    // ๋™๊ธฐํ™” : synchronized ์ถ”๊ฐ€
    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

๋ฌธ์ œ๋Š” ํ•ด๊ฒฐ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋™๊ธฐํ™”๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

๋™๊ธฐํ™”๊ฐ€ ๊ผญ ํ•„์š”ํ•œ ์‹œ์ ์€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹œ์ž‘๋˜๋Š” ๋•Œ ๋ฟ์ด๋‹ค.

uniqueInstance ๋ณ€์ˆ˜์— Singleton ์ธ์Šคํ„ด์Šค๋ฅผ ๋Œ€์ž…ํ•˜๊ณ  ๋‚˜๋ฉด ๊ตณ์ด ๋™๊ธฐํ™” ์ƒํƒœ๋กœ ์œ ์ง€์‹œํ‚ฌ ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

 

โ–  ๋” ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์€ ์—†์„๊นŒ?

1. getInstance()์˜ ์†๋„๊ฐ€ ๊ทธ๋ฆฌ ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด ๊ทธ๋ƒฅ ๋‘”๋‹ค.

getInstance() ๋ฉ”์†Œ๋“œ๊ฐ€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํฐ ๋ถ€๋‹ด์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ทธ๋ƒฅ ๋‘์ž. ๋™๊ธฐํ™”์‹œํ‚ค๋Š” ๊ฒƒ์ด ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š๊ณ , ํšจ์œจ์ ์œผ๋กœ ๊ดœ์ฐฎ์„ ์ˆ˜ ์žˆ๋‹ค. ๋‹จ, ๋ฉ”์†Œ๋“œ๋ฅผ ๋™๊ธฐํ™” ์‹œํ‚ค๋ฉด ์„ฑ๋Šฅ์ด 100๋ฐฐ ์ •๋„ ์ €ํ•˜๋œ๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•ด ๋‘์ž. ๋งŒ์•ฝ, getInstance()๊ฐ€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ณ‘๋ชฉ์œผ๋กœ ์ž‘์šฉ๋˜๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•ด์•ผ ํ•œ๋‹ค.

2. ์ธ์Šคํ„ด์Šค๋ฅผ ํ•„์š”ํ•  ๋•Œ ์ƒ์„ฑํ•˜์ง€๋ง๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด ๋ฒ„๋ฆฌ์ž. (์ด๋ฅธ ์ดˆ๊ธฐํ™” : eager initialization)

public class Singleton {
    // ์ด๋ฅธ ์ดˆ๊ธฐํ™” : ์ธ์Šคํ„ด์Šค๋ฅผ ์ฒ˜์Œ์— ์ƒ์„ฑ
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {}

    public static Singleton getInstance() {
        return uniqueInstance;
    }
}

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ฐ˜๋“œ์‹œ Singleton์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ•ญ์ƒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋˜๋Š” ์‹คํ–‰์ค‘์— ์ˆ˜์‹œ๋กœ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ๊ฐ€ ๋ฒˆ๊ฑฐ๋กญ๋‹ค๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ Singleton ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ฒ„๋ฆฌ์ž. ์ด๋Š” ํด๋ž˜์Šค๊ฐ€ ๋กœ๋”ฉ๋  ๋•Œ JVM์—์„œ Singleton์˜ ์œ ์ผํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

getInstance() ์—์„œ ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ”๋กœ ๋ฆฌํ„ดํ•ด์ฃผ๋ฏ€๋กœ ์“ฐ๋ ˆ๋“œ ์„ธ์ดํ”„ ํ•˜๋‹ค.
๋‹จ์ ์€ ๋ฏธ๋ฆฌ ๋งŒ๋“ ๋‹ค๋Š”๊ฒƒ ๊ทธ ์ž์ฒด๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์†Œ์Šค๋ฅผ์จ์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋‘์—ˆ๋Š”๋ฐ, ์‚ฌ์šฉํ•˜์ง€์•Š๋Š”๋‹ค๋ฉด ์ž์›๋‚ญ๋น„๊ฐ€ ๋œ๋‹ค.

 

๊ทธ๋ž˜์„œ ์“ฐ๋ ˆ๋“œ ์„ธ์ดํ”„ ํ•˜๋ฉด์„œ ์‚ฌ์šฉ์ด ๋  ๋•Œ ๋งŒ๋“ค๊ณ  ์‹ถ์–ด์ง„๋‹ค. (Thread Safety, Lazy Initialization)

๊ทธ๋Ÿฌ์ž๋‹ˆ synchronized ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋‹ˆ ๋น„์šฉ์ด ์‹ ๊ฒฝ์“ฐ์ธ๋‹ค.

 

3. DCL (Double-Checking Locking) ์„ ์จ์„œ getInstance() ์—์„œ ๋™๊ธฐํ™”๋˜๋Š” ๋ถ€๋ถ„์„ ์ค„์ด์ž.

public class Singleton {
    // volatile์„ ์‚ฌ์šฉ
    private volatile static Singleton uniqueInstance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

์•„๋ž˜ ๋ฐฉ๋ฒ•๋ณด๋‹ค ์™œ ํšจ์œจ์ ์ผ๊นŒ?

public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

getInstance() ๋ฅผ ํ˜ธ์ถœํ• ๋•Œ๋งˆ๋‹ค ๋™๊ธฐํ™” ๋งค์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋จผ์ € ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜์–ด์žˆ๋Š”์ง€ ํ™•์ธ ํ›„, ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์„๋•Œ๋งŒ synchronized ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋นˆ๋ฒˆํ•œ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด์„œ ๋”๋ธ” ์ฒดํฌ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ synchronized ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ์ ์œผ๋กœ ๋‹น์—ฐํžˆ ๋” ์šฐ์ˆ˜ํ•˜๋‹ค.

 

๋ฌธ์ œ๋Š” volatile ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

volatile ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋ฐ”์˜ ์ผ์ข…์˜ ์ตœ์ ํ™”์ธ ๋ฆฌ์˜ค๋”๋ง์„ ํšŒํ”ผํ•˜์—ฌ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ ์ˆœ์„œ๋ฅผ ๋ณด์žฅ, ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์“ฐ๋”๋ผ๋„ uniqueInstance๋ณ€์ˆ˜๊ฐ€ Singleton์ธ์Šคํ„ด์Šค๋กœ ์ดˆ๊ธฐํ™” ๋˜๋Š” ๊ณผ์ •์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ง„ํ–‰๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. (DCL์€ 1.5์ด์ƒ ๋ฒ„์ „์—์„œ๋งŒ ๊ฐ€๋Šฅ)

 

(์ฐธ๊ณ ) ๋ฆฌ์˜ค๋”๋ง : ๋ณดํ†ต ์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ ์ผ์–ด๋‚˜๋ฉฐ, ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ ์ฝ”๋“œ๋Š” ์ปดํŒŒ์ผ ๋  ๋•Œ ์ข€๋” ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ์กฐ์ž‘์ด ๊ฐ€ํ•ด์ ธ ์ตœ์ ํ™” ๋จ

→ ๋ชจ๋“  JVM๊ตฌํ˜„์—์„œ ์ž‘๋™ํ•˜๋Š”๊ฒƒ์„ ๋ณด์žฅํ•  ์ˆ˜๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์— DCL์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๊ฒŒ ์ข‹๋‹ค.

 

4. static inner ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

public class Singleton {

    private Singleton() {}
    
    // static inner ํด๋ž˜์Šค ์‚ฌ์šฉ
    private static class SingletonHolder {
        private static final Singleton UNIQUE_INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.UNIQUE_INSTANCE;
    }
}

๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๋ฉฐ, getInstance() ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์ƒ์„ฑ๋˜๋ฏ€๋กœ Lazy Initialization ์ด๋‹ค.

 

UML Class Diagram

 

๋ฐ˜์‘ํ˜•