π‘ μ±κΈν€(Singleton) ν¨ν΄μ΄λ?
ν΄λμ€μ μΈμ€ν΄μ€κ° νλλ§ μλλ‘ νλ©΄μ μ΄ μΈμ€ν΄μ€μ λν μ μ μ κ·Ό(μ‘μΈμ€) μ§μ μ μ 곡νλ μμ± λμμΈ ν¨ν΄μ λλ€. μ½κ² λ§ν΄, λ¨ νλμ μ μΌν κ°μ²΄λ₯Ό λ§λ€κΈ° μν μ½λ ν¨ν΄μ΄λ©° μΈμ€ν΄μ€κ° νμν λ λκ°μ μΈμ€ν΄μ€λ₯Ό μλ‘ λ§λ€μ§ μκ³ κΈ°μ‘΄μ μΈμ€ν΄μ€λ₯Ό κ°μ Έμ νμ©νλ κΈ°λ²μ λ§ν©λλ€.
λ³΄ν΅ μ±κΈν€ ν¨ν΄μ΄ μ μ©λ κ°μ²΄κ° νμν κ²½μ°λ κ·Έ κ°μ²΄κ° 리μμ€λ₯Ό λ§μ΄ μ°¨μ§νλ μν μ νλ λ¬΄κ±°μ΄ ν΄λμ€μΌλ μ ν©ν©λλ€. λνμ μΌλ‘ λ°μ΄ν°λ² μ΄μ€ μ°κ²° λͺ¨λμ μλ‘ λ€ μ μλλ°, λ°μ΄ν°λ² μ΄μ€μ μ μνλ μμ (I/O λ°μ΄λ)μ κ·Έ μμ²΄λ‘ λ¬΄κ±°μ΄ μμ μ μνλ©° λν νλ²λ§ κ°μ²΄λ₯Ό μμ±νκ³ λλ €μ°λ©΄ λμ§ κ΅³μ΄ μ¬λ¬λ² μμ±ν νμκ° μκΈ° λλ¬Έμ λλ€.
π‘ ν΄λμ€ λ€μ΄μ΄κ·Έλ¨μΌλ‘ λ³Έ μ±κΈν€ ν¨ν΄
- Singleton ν΄λμ€λ μ μ λ©μλ getInstanceλ₯Ό μ μΈν©λλ€. μ΄ λ©μλλ μ체 ν΄λμ€μ κ°μ μΈμ€ν΄μ€λ₯Ό λ°νν©λλ€.
- λ€λ₯Έ κ°μ²΄λ€μ΄ μμ±μλ₯Ό μ μΈνμ§ λͺ»νλλ‘, default μμ±μλ privateμΌλ‘ μ μΈν©λλ€. (λ€λ₯Έ κ°μ²΄λ€μ μμ±μ λμ getInstanceλ₯Ό μ¬μ©ν©λλ€.)
public class Main {
public static void main(String[] args) {
// Singleton.getInstance() λ₯Ό ν΅ν΄ μ±κΈν€ κ°μ²΄λ₯Ό κ°κΈ° λ³μλ§λ€ λ°μμλ λκ°μ κ°μ²΄ μ£Όμλ₯Ό κ°λ¦¬ν΄
Singleton i1 = Singleton.getInstance();
Singleton i2 = Singleton.getInstance();
Singleton i3 = Singleton.getInstance();
System.out.println(i1.toString()); // Singleton@1b6d3586
System.out.println(i2.toString()); // Singleton@1b6d3586
System.out.println(i3.toString()); // Singleton@1b6d3586
System.out.println(i1 == i2); // true
}
}
π‘ μ±κΈν€ ν¨ν΄ ꡬν
ꡬν κΈ°λ²μλ μλμ κ°μ΄ 7κ°μ§κ° μ‘΄μ¬ν©λλ€.
- Eager Initialization
- Static Block Initialization
- Lazy Initialization
- Thread Safe Initialization
- Double-Checked Locking
- Bill Pugh Solution β
- Enum β
νλμ© μμλ΄ μλ€.
1. Eager Initialization(μ΄λ₯Έ μ΄κΈ°ν)
νλ²λ§ 미리 λ§λ€μ΄λλ, κ°μ₯ μ§κ΄μ μ΄λ©΄μλ μ¬νν κΈ°λ²μ λλ€. κ°μ²΄κ° 그리 ν¬μ§ μμ κ°μ²΄μΈ κ²½μ° ν΄λΉ κΈ°λ²μ μ¬μ©ν μ μμ΅λλ€.
μ₯μ : static finalλ‘ μ μΈνμ¬ λ©ν° μ°λ λ νκ²½μμλ μμ ν©λλ€.
λ¨μ : λΉμ₯ κ°μ²΄λ₯Ό μ¬μ©νμ§ μλλΌλ λ©λͺ¨λ¦¬μ μ μ¬νλ staticμ μ¬μ©νμ¬ λ¦¬μμ€κ° ν° κ°μ²΄μΌ κ²½μ° κ³΅κ° μμ λλΉκ° λ°μν©λλ€.
class Singleton {
// μ±κΈν€ ν΄λμ€ κ°μ²΄λ₯Ό λ΄μ μΈμ€ν΄μ€ λ³μ
private static final Singleton INSTANCE = new Singleton();
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
2. Static Block Initialization(μ μ λΈλ‘ μ΄κΈ°ν)
static blockμ΄λ, ν΄λμ€κ° λ‘λ©λκ³ ν΄λμ€ λ³μκ° μ€λΉλ ν μλμΌλ‘ μ€νλλ λΈλ‘μ μλ―Έν©λλ€.
μ₯μ : static blockμ μ΄μ©νμ¬ μμΈλ₯Ό μ‘μ μ μμ΅λλ€.
λ¨μ : λΉμ₯ κ°μ²΄λ₯Ό μ¬μ©νμ§ μλλΌλ λ©λͺ¨λ¦¬μ μ μ¬νλ staticμ μ¬μ©νμ¬ λ¦¬μμ€κ° ν° κ°μ²΄μΌ κ²½μ° κ³΅κ° μμ λλΉκ° λ°μν©λλ€.
class Singleton {
// μ±κΈν€ ν΄λμ€ κ°μ²΄λ₯Ό λ΄μ μΈμ€ν΄μ€ λ³μ
private static Singleton instance;
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
// static λΈλ‘μ μ΄μ©ν΄ μμΈ μ²λ¦¬
static {
try {
instance = new Singleton();
} catch (Exception e) {
throw new RuntimeException("μ±κΈν€ κ°μ²΄ μμ± μ€λ₯");
}
}
public static Singleton getInstance() {
return instance;
}
}
3. Lazy Initialization(μ§μ° μ΄κΈ°ν)
κ°μ²΄ μμ±μ λν κ΄λ¦¬λ₯Ό λ΄λΆμ μΌλ‘ μ²λ¦¬ν©λλ€. λ©μλλ₯Ό νΈμΆνμ λ μΈμ€ν΄μ€ λ³μμ null μ 무μ λ°λΌ μ΄κΈ°ννκ±°λ, μλ μΈμ€ν΄μ€ λ³μλ₯Ό λ°νν©λλ€.
μ₯μ : κ³ μ λ©λͺ¨λ¦¬ μ°¨μ§μ νκ³μ μ 극볡ν©λλ€. (κ°μ²΄λ₯Ό μ¬μ©ν λ λ©λͺ¨λ¦¬μ μ μ¬)
λ¨μ : Thread Safeνμ§ μμ΅λλ€. λ©ν° μ€λ λ νκ²½μμ λμμ± λ¬Έμ κ° λ°μν©λλ€.(μμμ± κ²°μ¬)
class Singleton {
// μ±κΈν€ ν΄λμ€ κ°μ²΄λ₯Ό λ΄μ μΈμ€ν΄μ€ λ³μ
private static Singleton instance;
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
// μΈλΆμμ μ μ λ©μλλ₯Ό νΈμΆνλ©΄ κ·Έμ μμΌ μ΄κΈ°ν μ§ν(lazy)
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton(); // μ€μ§ 1κ°μ κ°μ²΄λ§ μμ±
}
return instance;
}
}
4. Thread Safe Initialization
μμ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄, synchronized ν€μλλ₯Ό ν΅ν΄ λ©μλμ μ°λ λλ€μ νλμ© μ κ·Όν μ μλλ‘ λκΈ°ν μν€λ λ°©λ²μ λλ€.
μ₯μ : Thread Safeν©λλ€.
λ¨μ : μ¬λ¬ κ°μ λͺ¨λλ€μ΄ κ°μ²΄λ₯Ό κ°μ Έμ¬ λ λ§€λ² synchronized λ©μλλ₯Ό νΈμΆνμ¬, overheadλ‘ μΈν μ±λ₯ νλ½μ΄ λ°μν©λλ€.
π‘ synchronized ν€μλλ λ©ν° μ°λ λ νκ²½μμ λκ° μ΄μμ μ°λ λκ° νλμ λ³μμ λμμ μ κ·Όμ ν λ Race condition(κ²½μμν)μ΄ λ°μνμ§ μλλ‘ ν©λλ€. νλ§λλ‘ μ°λ λκ° ν΄λΉ λ©μλλ₯Ό μ€ννλ λμ λ€λ₯Έ μ°λ λκ° μ κ·Όνμ§ λͺ»νλλ‘ μ κΈ(lock)μ κ±°λ κ²μ λλ€.
class Singleton {
// μ±κΈν€ ν΄λμ€ κ°μ²΄λ₯Ό λ΄μ μΈμ€ν΄μ€ λ³μ
private static Singleton instance;
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
// μΈλΆμμ μ μ λ©μλλ₯Ό νΈμΆνλ©΄ κ·Έμ μμΌ μ΄κΈ°ν μ§ν(lazy)
// synchronized λ©μλ
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton(); // μ€μ§ 1κ°μ κ°μ²΄λ§ μμ±
}
return instance;
}
}
5. Double-Checked Locking -> μ¬μ© μ§μ
μμ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄, μ΅μ΄ μ΄κΈ°νν λλ§ synchronizedλ₯Ό μ μ©νκ³ , μ΄λ―Έ λ§λ€μ΄μ§ μΈμ€ν΄μ€λ₯Ό λ°νν λλ μ¬μ©νμ§ μλλ‘ νλ κΈ°λ²μ λλ€. μ΄ λ, μΈμ€ν΄μ€ νλμ volatile ν€μλλ₯Ό μ¬μ©ν©λλ€.(I/O λΆμΌμΉ λ¬Έμ ν΄κ²°)
μ₯μ : λ§€λ² synchronized νΈμΆλ‘ μΈν μ±λ₯ νλ½ λ°©μ§
λ¨μ : JVM λ²μ μ΄ 1.5 μ΄μμ΄μ΄μΌ νλ©°, JVMμ λ°λΌ Thread Safeνμ§ μλ κ²½μ°κ° λ°μν©λλ€. → μ¬μ© μ§μ
π‘ Javaμμλ μ°λ λλ₯Ό μ¬λ¬κ° μ¬μ©ν κ²½μ°, μ±λ₯μ μν΄μ κ°κ°μ μ°λ λλ€μ λ³μλ₯Ό λ©μΈ λ©λͺ¨λ¦¬(RAM)μΌλ‘λΆν° κ°μ Έμ€λ κ²μ΄ μλλΌ μΊμ(Cache) λ©λͺ¨λ¦¬μμ κ°μ Έμ€κ² λ©λλ€. λ¬Έμ λ λΉλκΈ°λ‘ λ³μκ°μ μΊμμ μ μ₯νλ€κ°, κ° μ°λ λλ§λ€ ν λΉλμ΄μλ μΊμ λ©λͺ¨λ¦¬μ λ³μκ°μ΄ μΌμΉνμ§ μμ μ μμ΅λλ€. μ΄ κ²½μ° volatile ν€μλλ₯Ό ν΅ν΄ μ΄ λ³μλ μΊμμμ μ½μ§ λ§κ³ λ©μΈ λ©λͺ¨λ¦¬μμ μ½μ΄μ€λλ‘ μ§μ ν΄μ€ μ μμ΅λλ€.
class Singleton {
// μ±κΈν€ ν΄λμ€ κ°μ²΄λ₯Ό λ΄μ μΈμ€ν΄μ€ λ³μ
// volatile ν€μλ μ μ©
private static volatile Singleton instance;
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
// μΈλΆμμ μ μ λ©μλλ₯Ό νΈμΆνλ©΄ κ·Έμ μμΌ μ΄κΈ°ν μ§ν(lazy)
public static Singleton getInstance() {
if(instance == null) {
// synchronizedλ₯Ό λ©μλκ° μλ Singleton ν΄λμ€ μ체μ 건λ€.
synchronized (Singleton.class)
if(instance == null) {
instance = new Singleton(); // μ€μ§ 1κ°μ κ°μ²΄λ§ μμ±
}
}
return instance;
}
}
6. Bill Pugh Solution (LazyHolder) β
κΆμ₯λλ λ κ°μ§ λ°©λ² μ€ νλλ‘, λ©ν°μ°λ λ νκ²½μμ μμ νκ³ Lazy Loadingλ κ°λ₯ν μλ²½ν μ±κΈν€ κΈ°λ²μ λλ€. ν΄λμ€ μμ λ΄λΆ ν΄λμ€(holder)λ₯Ό λμ΄, JVMμ ν΄λμ€ λ‘λ 맀컀λμ¦κ³Ό ν΄λμ€κ° λ‘λλλ μμ μ μ΄μ©ν©λλ€.
μ₯μ : μμ μΈκΈν λͺ¨λ λ¬Έμ ν΄κ²°
λ¨μ : ν΄λΌμ΄μΈνΈκ° μμλ‘ μ±κΈν€μ νκ΄΄ν μ μμ΅λλ€. (Reflection API, μ§λ ¬ν/μμ§λ ¬νλ₯Ό ν΅ν΄)
class Singleton {
// μμ±μλ₯Ό privateλ‘ μ μΈ (μΈλΆμμ new μ¬μ© X)
private Singleton() {}
// static λ΄λΆ ν΄λμ€λ₯Ό μ΄μ©
// Holderλ‘ λ§λ€μ΄, ν΄λμ€κ° λ©λͺ¨λ¦¬μ λ‘λλμ§ μκ³ getInstance λ©μλ νΈμΆ μμ λ‘λλ¨.
private static class SingleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
//
public static Singleton getInstance() {
return SingleInstanceHolder.INSTANCE;
}
}
7. Enum β
κΆμ₯λλ λ κ°μ§ λ°©λ² μ€ νλλ‘, λ©ν°μ°λ λ νκ²½μμ μμ νλ©°, ν΄λΌμ΄μΈνΈμμ Reflectionμ ν΅ν 곡격μλ μμ ν©λλ€.
μ₯μ : μμ μΈκΈν λͺ¨λ λ¬Έμ ν΄κ²°
λ¨μ : μ±κΈν€ ν΄λμ€λ₯Ό λ©ν°ν€(μΌλ° ν΄λμ€)λ‘ Migration ν΄μΌ ν λ μ²μλΆν° μ½λλ₯Ό λ€μ μ§μΌ ν©λλ€. λν, ν΄λμ€ μμμ΄ νμν λ, enum type μΈμ ν΄λμ€ μμμ λΆκ°λ₯ν©λλ€.
enum SingletonEnum {
INSTANCE;
private final Client dbClient;
SingletonEnum() {
dbClient = Database.getClient();
}
public static SingletonEnum getInstance() {
return INSTANCE;
}
public Client getClient() {
return dbClient;
}
}
public class Main {
public static void main(String[] args) {
SingletonEnum singleton = SingletonEnum.getInstance();
singleton.getClient();
}
}
κ²°λ‘ μ μΌλ‘λ, LaszHolder λ°©λ²μ μ±λ₯μ΄ μ€μμ λλ νκ²½μμ, Enum λ°©λ²μ μ§λ ¬νμ μμ μ±μ΄ μ€μμ λλ νκ²½μμ μ¬μ©νλ©΄ μ’λ€.
π‘ λ§λ¬΄λ¦¬
μ±κΈν€ ν¨ν΄μ
- νλ‘κ·Έλ¨μ ν΄λμ€μ λͺ¨λ ν΄λΌμ΄μΈνΈκ° μ¬μ©ν μ μλ λ¨μΌ μΈμ€ν΄μ€λ§ μμ΄μΌ ν λ(ex. λ¨μΌ λ°μ΄ν°λ² μ΄μ€ κ°μ²΄)
- μ μ λ³μλ€μ λ μ격νκ² μ μ΄ν΄μΌ ν λ
μ μ¬μ©ν μ μμ΅λλ€.
μ€μ§ ν κ°μ μΈμ€ν΄μ€ μμ±μ 보μ¦νμ¬ ν¨μ¨μ μ°Ύμ μ μμ§λ§, λͺ¨λ κ°μ μμ‘΄μ±μ΄ λμμ§κ³ , SRP, OCP, DIPλ₯Ό μλ°ν©λλ€.(κ·Έλμ OOPμ μν° ν¨ν΄μ΄λΌκ³ λ λΆλ¦°λ€.) λν, TDD λ¨μ ν μ€νΈμ μ λ‘μ¬νμ΄ μμ΅λλ€.
λ°λΌμ μ±κΈν€ ν¨ν΄μ μ§μ λ§λ€μ΄ μ¬μ©νλ κ²λ³΄λ€λ, Spring Containerμ κ°μ νλ μμν¬μ λμμ λ°μΌλ©΄ μ±κΈν€ ν¨ν΄μ λ¬Έμ μ λ€μ 보μνλ©΄μ μ₯μ μ λ릴 μ μμ΅λλ€.
μ€νλ§ νλ μμν¬μμλ μ±κΈν€ ν¨ν΄μ΄λκ² μκ³ λ΄λΆμ μΌλ‘ ν΄λμ€μ μ μ΄λ₯Ό Ioc(Inversion Of Control) λ°©μμ 컨ν μ΄λμκ² λ겨 컨ν μ΄λκ° κ΄λ¦¬ν©λλ€. λ°λΌμ, μΌλ° κ°μ²΄λ νλμ μΈμ€ν΄μ€ λΏμΈ μ±κΈν€μΌλ‘ μ‘΄μ¬κ° κ°λ₯ν©λλ€.