์๋ฐ ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ํ๋ก๊ทธ๋จ์ ๋ง๋ค๋ค ๋ณด๋ฉด, ์๋์น ์์ ๊ฒฐ๊ณผ๊ฐ ๋์ค๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ด๋ฐ ์ํฉ์์ ์์ฃผ ๋ฑ์ฅํ๋ ํค์๋๊ฐ ์๋ค. ๋ฐ๋ก synchronized๋ค. ์ค๋์ synchronized๋ฅผ ์กฐ๊ธ ๋ ๊น๊ฒ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.

synchronized๋ฅผ ์ดํดํ๋ ค๋ฉด ๋จผ์ ์๋ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์๋ ๊ฒ์ด ๋์์ด ๋๋ค.
์๋ฐ ๋ฉ๋ชจ๋ฆฌ๋ ํฌ๊ฒ Stack, Heap, Method Area๋ก ๊ตฌ๋ถ๋๋ค. ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์๋ ์ค๋ ๋๋ค์ด Heap๊ณผ Method Area๋ฅผ ๊ณต์ ํ์ง๋ง, Stack์ ๊ฐ ์ค๋ ๋๋ง๋ค ๊ณ ์ ํ๊ฒ ์กด์ฌํ๋ค.
๋ฐ๋ผ์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ณต์ ์์์ ์ ๊ทผํ ๋๋ ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ์ฃผ์ํด์ผ ํ๋ค.
์ด๋ฌํ ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ฃผ๋ ๊ฒ์ด ๋ฐ๋ก synchronized ํค์๋๋ค.
๋ฉ์๋ ์์ญ: ์ ์ญ ๋ณ์ , static ๋ณ์ ์ ์ฅ, ํ๋ก๊ทธ๋จ ์์๋ถํฐ ์ข ๋ฃ๊น์ง ๋ฉ๋ชจ๋ฆฌ์ ๋จ์์๋ค.
์คํ ์์ญ: ์ง์ญ ๋ณ์, ๋งค๊ฐ๋ณ์ ๋ฐ์ดํฐ ๊ฐ์ด ์ ์ฅ๋๋ ๊ณต๊ฐ, ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ ๋ฉ๋ชจ๋ฆฌ์ ํ ๋น ๋๊ณ ์ข ๋ฃ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ํด์ ๋๋ค.
ํ ์์ญ: new ํค์๋๋ก ์์ฑ๋๋ ๊ฐ์ฒด(์ธ์คํด์ค), ๋ฐฐ์ด ๋ฑ์ด ์ ์ฅ๋๋ฉฐ, ๊ฐ๋น์ง ์ปฌ๋ ์ ์ ์ํด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๊ด๋ฆฌ๋๋ค.
public static void main(String[] args) {
SyncDemo demo = new SyncDemo();
Thread tA = new Thread(() -> {
System.out.println("Thread-A ์์: " + LocalDateTime.now());
demo.firstMethod("Thread-A");
System.out.println("Thread-A ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread tB = new Thread(() -> {
System.out.println("Thread-B ์์: " + LocalDateTime.now());
demo.secondMethod("Thread-B");
System.out.println("Thread-B ์ข
๋ฃ: " + LocalDateTime.now());
});
tA.start();
tB.start();
}
private synchronized void firstMethod(String name) {
System.out.println(name + " -> firstMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void secondMethod(String name) {
System.out.println(name + " -> secondMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
SyncDemo ์ธ์คํด์ค๋ฅผ ํ๊ฐ ์์ฑํ๊ณ , ๋ ๊ฐ์ ์ค๋ ๋๋ฅผ ๋ง๋ค์ด ๊ฐ๊ฐ synchronized ํค์๋๊ฐ ๋ถ์ ThreadA, ThreadB ๋ฅผ ํธ์ถํด๋ณด์๋ค.

ํ์ธ ๊ฒฐ๊ณผ, ThreadA ํธ์ถ -> ์ข
๋ฃ ๋ ํ, ๋ค์ ThreadB ๊ฐ ํธ์ถ -> ์ข
๋ฃ ๋๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
๋ง์ฝ ์ธ์คํด์ค๋ฅผ ๊ฐ๊ฐ ๋ง๋ค๊ณ , ์ค๋ ๋๋ค์ ํธ์ถํ๊ฒ ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
public static void main(String[] args) {
SyncDemo demo1 = new SyncDemo();
SyncDemo demo2 = new SyncDemo();
Thread tA = new Thread(() -> {
System.out.println("Thread-A ์์: " + LocalDateTime.now());
demo1.firstMethod("Thread-A");
System.out.println("Thread-A ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread tB = new Thread(() -> {
System.out.println("Thread-B ์์: " + LocalDateTime.now());
demo2.secondMethod("Thread-B");
System.out.println("Thread-B ์ข
๋ฃ: " + LocalDateTime.now());
});
tA.start();
tB.start();
}
private synchronized void firstMethod(String name) {
System.out.println(name + " -> firstMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void secondMethod(String name) {
System.out.println(name + " -> secondMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

์ด ๊ฒฝ์ฐ, ๊ฐ ์ค๋ ๋๋ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด์ lock์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ค๋ ๋ ๊ฐ ๋๊ธฐํ๊ฐ ๋ฐ์ํ์ง ์๋๋ค. ๊ฒฐ๊ณผ์์ ํ์ธํ ์ ์๋ฏ์ด, synchronized ๋ฉ์๋๋ ํด๋น ์ธ์คํด์ค์ lock์ ๊ฑด๋ค.
์ฌ๊ธฐ์ โ์ธ์คํด์ค์ lock์ ๊ฑด๋คโ๋ ํํ ๋๋ฌธ์, ๋ง์น ์ธ์คํด์ค ์์ฒด์ ์ ๊ทผ์ด ๋ถ๊ฐ๋ฅํด์ง๋ ๊ฒ์ฒ๋ผ ์คํดํ ์ ์์ง๋ง, ์ค์ ๋ก๋ ๋ฉ์๋ ์คํ ์์๋ง lock์ด ์ ์ฉ๋๋ค.
ํ๋ฒ ์ฝ๋๋ก ํ์ธํด๋ณด์!!
public static void main(String[] args) {
SyncDemo demo = new SyncDemo(); // ํ๋์ ๊ฐ์ฒด๋ง ์์ฑ
Thread tA = new Thread(() -> {
System.out.println("Thread-A ์์: " + LocalDateTime.now());
demo.firstMethod("Thread-A");
System.out.println("Thread-A ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread tB = new Thread(() -> {
System.out.println("Thread-B ์์: " + LocalDateTime.now());
demo.secondMethod("Thread-B");
System.out.println("Thread-B ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread tC = new Thread(() -> {
System.out.println("Thread-C ์์: " + LocalDateTime.now());
demo.thirdMethod("Thread-C");
System.out.println("Thread-C ์ข
๋ฃ: " + LocalDateTime.now());
});
tA.start();
tB.start();
tC.start();
}
private synchronized void firstMethod(String name) {
System.out.println(name + " -> firstMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void secondMethod(String name) {
System.out.println(name + " -> secondMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void thirdMethod(String name) {
System.out.println(name + " -> thirdMethod ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

์ด ์ฝ๋์์ Thread-C๋ thirdMethod๋ฅผ ์คํํ๋๋ฐ, ์ด ๋ฉ์๋๋ synchronized๊ฐ ๋ถ์ง ์์ ์ผ๋ฐ ๋ฉ์๋๋ค.
๋ฐ๋ผ์ Thread-C์๋ ๋ฝ์ด ๊ฑธ๋ฆฌ์ง ์๊ณ , ๋ค๋ฅธ ์ค๋ ๋(firstMethod, secondMethod)์ ๋์์ ์คํ๋ ์ ์์์ ํ์ธํ ์ ์๋ค.
๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด, synchronized ๋ฉ์๋๋ ๊ฐ์ฒด(์ธ์คํด์ค) ๋จ์๋ก ๋ฝ์ ๊ฑด๋ค.
ํ์ง๋ง ๋ฝ์ synchronized ํค์๋๊ฐ ๋ถ์ ๋ฉ์๋๋ผ๋ฆฌ๋ง ๊ณต์ ๋๋ฏ๋ก, ์ผ๋ฐ ๋ฉ์๋์๋ ๋ฝ์ด ์ ์ฉ๋์ง ์๋๋ค.
์๋ก ๋ค๋ฅธ ๊ฐ์ฒด์ synchronized ๋ฉ์๋๋ ๊ฐ๊ฐ ๋ณ๋์ ๋ฝ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์์ ์คํ๋ ์ ์๋ค.
๋ฐ๋ผ์, synchronized ๋ฉ์๋ ๋๊ธฐํ๋ ๊ฐ์ฒด ๋จ์๋ผ๋ ์ ์ด ์ค์ํ๋ค.
๋๊ธฐํ ๋ฒ์๋ synchronized ๋ฉ์๋์ ํ์
์ผ๋ฐ ๋ฉ์๋๋ ๋ฝ๊ณผ ์๊ด์์ด ์ธ์ ๋ ๋์์ ์คํ ๊ฐ๋ฅํ๋ค.
๋ฝ์ synchronized๊ฐ ๋ถ์ ๋ฉ์๋๋ผ๋ฆฌ๋ง ๊ณต์ ๋๋ค.
public static void main(String[] args) {
Thread tA = new Thread(() -> {
System.out.println("Thread-A ์์: " + LocalDateTime.now());
syncMethodA("Thread-A");
System.out.println("Thread-A ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread tB = new Thread(() -> {
System.out.println("Thread-B ์์: " + LocalDateTime.now());
syncMethodB("Thread-B");
System.out.println("Thread-B ์ข
๋ฃ: " + LocalDateTime.now());
});
tA.start();
tB.start();
}
public static synchronized void syncMethodA(String name) {
System.out.println(name + " -> syncMethodA ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void syncMethodB(String name) {
System.out.println(name + " -> syncMethodB ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด, static ํค์๋๊ฐ ๋ถ์
synchronized ๋ฉ์๋๋ ์ธ์คํด์ค๊ฐ ์๋ ํด๋์ค ๋จ์๋ก lock์ ๊ณต์ ํ๋ค.
๋ฐ๋ผ์ static synchronized๋ฅผ ์ฌ์ฉํ๋ฉด, ์ฌ๋ฌ ๊ฐ์ฒด๊ฐ ์กด์ฌํด๋ ํด๋์ค ๋จ์๋ก ์์ ํ๊ฒ ๋๊ธฐํ๋ฅผ ๋ณด์ฅํ ์ ์๋ค.
์ฌ๊ธฐ๋ค๊ฐ ์ผ๋ฐ synchronized ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
public static void main(String[] args) {
StaticLockDemo demo = new StaticLockDemo();
Thread t1 = new Thread(() -> {
System.out.println("Thread-1 ์์: " + LocalDateTime.now());
syncStaticA("Thread-1");
System.out.println("Thread-1 ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread t2 = new Thread(() -> {
System.out.println("Thread-2 ์์: " + LocalDateTime.now());
syncStaticB("Thread-2");
System.out.println("Thread-2 ์ข
๋ฃ: " + LocalDateTime.now());
});
Thread t3 = new Thread(() -> {
System.out.println("Thread-3 ์์: " + LocalDateTime.now());
demo.syncC("Thread-3");
System.out.println("Thread-3 ์ข
๋ฃ: " + LocalDateTime.now());
});
t1.start();
t2.start();
t3.start();
}
public static synchronized void syncStaticA(String name) {
System.out.println(name + " -> syncStaticA ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void syncStaticB(String name) {
System.out.println(name + " -> syncStaticB ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private synchronized void syncC(String name) {
System.out.println(name + " -> syncC ์คํ ์ค: " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

syncStaticA ๊ณผ syncStaticB๋ ๋๊ธฐํ๊ฐ ์ ์ง์ผ์ง๋๊ฑธ ํ์ธ ํ ์ ์์ง๋ง ์ผ๋ฐ synchronized์ธ syncC๋ ์ง์ผ์ง์ง ์๋๊ฑธ ํ์ธ ํ ์ ์๋ค.
ํด๋์ค ๋จ์๋ก ๋ฝ์ ๊ฑฐ๋ static synchronized์ ์ธ์คํด์ค ๋จ์๋ก ๋ฝ์ ๊ฑฐ๋ ์ผ๋ฐ synchronized๋ฅผ ํผ์ฉํ ๊ฒฝ์ฐ, ์ด๋ ๋ฝ์ด ์ ์ฉ๋๋์ง ํท๊ฐ๋ฆด ์ ์์ผ๋ฏ๋ก ์ฃผ์ํ์.
synchronized block์ ๋ฉ์๋ ์ ์ฒด๊ฐ ์๋, ์ฝ๋ ๋ธ๋ก ๋จ์๋ก ๋๊ธฐํ๋ฅผ ์ ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
public static void main(String[] args) {
SyncBlockDemo demo = new SyncBlockDemo();
Thread t1 = new Thread(() -> {
System.out.println("Thread-A ์์ " + LocalDateTime.now());
demo.lockBlockA("Thread-A");
System.out.println("Thread-A ์ข
๋ฃ " + LocalDateTime.now());
});
Thread t2 = new Thread(() -> {
System.out.println("Thread-B ์์ " + LocalDateTime.now());
demo.lockBlockB("Thread-B");
System.out.println("Thread-B ์ข
๋ฃ " + LocalDateTime.now());
});
t1.start();
t2.start();
}
private void lockBlockA(String name) {
synchronized (this) {
System.out.println(name + " -> lockBlockA ์คํ ์ค " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void lockBlockB(String name) {
synchronized (this) {
System.out.println(name + " -> lockBlockB ์คํ ์ค " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

์์ ๊ฐ์ด synchronized ๋ธ๋ก์์ this๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ชจ๋ ํด๋น ๊ฐ์ฒด์ synchronized ๋ธ๋ก์ ๊ฐ์ ๋ฝ์ด ๊ฑธ๋ฆฌ๊ฒ ๋๋ค.
์ฆ, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅธ synchronized ๋ธ๋ก์ ํธ์ถํ๋๋ผ๋, this์ ๋ฝ์ด ๊ฑธ๋ ค ์๊ธฐ ๋๋ฌธ์ ์์ฐจ์ ์ผ๋ก ์คํํด์ผ ํ๋ค.
public class LockBlockDemo {
private final Object lockA = new Object();
private final Object lockB = new Object();
public static void main(String[] args) {
LockBlockDemo demo = new LockBlockDemo();
Thread t1 = new Thread(() -> {
System.out.println("Thread-1 ์์ " + LocalDateTime.now());
demo.blockA("Thread-1");
System.out.println("Thread-1 ์ข
๋ฃ " + LocalDateTime.now());
});
Thread t2 = new Thread(() -> {
System.out.println("Thread-2 ์์ " + LocalDateTime.now());
demo.blockB("Thread-2");
System.out.println("Thread-2 ์ข
๋ฃ " + LocalDateTime.now());
});
t1.start();
t2.start();
}
private void blockA(String name) {
synchronized (lockA) {
System.out.println(name + " -> blockA ์คํ ์ค " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void blockB(String name) {
synchronized (lockB) {
System.out.println(name + " -> blockB ์คํ ์ค " + LocalDateTime.now());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Thread-1๊ณผ Thread-2๊ฐ ๋์์ ์คํ๋๋ ๊ฒ์ ๋ณด๋ฉด, ๋๊ธฐํ๊ฐ ์ ์ฉ๋์ง ์์ ๊ฒ์ ํ์ธํ ์ ์๋ค.
this ๋์ lockA, lockB์ ๊ฐ์ ๋ณ๋์ ๊ฐ์ฒด๋ฅผ ๋ฝ์ผ๋ก ์ฌ์ฉํ๋ฉด, ๋์์ ๋ฝ์ ๊ฑธ์ด์ผ ํ๋ ๋ถ๋ถ์ ๊ฐ๋ณ์ ์ผ๋ก ์ง์ ํ ์ ์๋ค.
public class SimpleSingleton {
private static SimpleSingleton sSimpleSingleton;
public static SimpleSingleton getInstance() {
if (Objects.isNull(sSimpleSingleton)) {
sSimpleSingleton = new SimpleSingleton();
}
return sSimpleSingleton;
}
}
์ฑ๊ธํค ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ผ๋ก ์ฝ๋๋ฅผ ์ง๋ดค๋ค.
ํ์ง๋ง ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ getInstance()๋ฅผ ํธ์ถํ๋ฉด ๋์ ์ ๊ทผ ๋ฌธ์ (race condition) ๋ฐ์ ๊ฐ๋ฅํ๋ค.
์ฌ๊ธฐ์ ์ด์ ๋๊ฐ์ง ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ์กด์ฌํ๋ค.
๋ฐ๋ก ๋ฉ์๋์ synchronized ํค์๋๋ฅผ ๋ถ์ด๊ฑฐ๋, Double-Checked Locking (DCL) , LazyHolder๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ผ๋จ synchronized ํค์๋ ๋ฅผ ๋ถ์ฌ๋ณด์
public static synchronized SimpleSingleton getInstance() {
if (Objects.isNull(sSimpleSingleton)) {
sSimpleSingleton = new SimpleSingleton();
}
return sSimpleSingleton;
}
ํ์ง๋ง ์ธ์คํด์ค๊ฐ ์ด๋ฏธ ์์ฑ๋ ์ดํ์๋, ๋ชจ๋ ์ค๋ ๋๊ฐ synchronized๋ฅผ ๊ฑฐ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์ํ ์ ์๋ค.
ํนํ getInstance()๋ฅผ ์์ฃผ ํธ์ถํ๋ ์ํฉ์์๋, ๋ถํ์ํ ๋ฝ ๋น์ฉ์ด ๋ฐ๋ณต์ ์ผ๋ก ๋ฐ์ํ๊ฒ ๋๋ค.
public class DCLSingleton {
// volatile ํค์๋๋ก ๋ฉํฐ์ค๋ ๋์์ ์์ ํ๊ฒ
private static volatile DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) { // 1์ฐจ ์ฒดํฌ (๋ฝ ๊ฑธ๊ธฐ ์ )
synchronized (DCLSingleton.class) { // ํด๋์ค ๋จ์๋ก ๋ฝ
if (instance == null) { // 2์ฐจ ์ฒดํฌ (๋ฝ ์์์)
instance = new DCLSingleton();
}
}
}
return instance;
}
}
Double-Checked Locking(DCL)์ ๋ฉ์๋ ์ ์ฒด์ synchronized๋ฅผ ๊ฑธ์ง ์๊ณ , ๋๊ธฐํ ์ค๋ฒํค๋๋ฅผ ์ค์ด๊ธฐ ์ํด ์ค๊ณ๋ ๋ฐฉ์์ด๋ค.
์ต์ด ์ธ์คํด์ค๊ฐ ์์ฑ๋ ์ดํ์๋ ๋๊ธฐํ ๋ธ๋ก์ ์ง์ ํ์ง ์์ ์ฑ๋ฅ์ ์ต์ ํ ์๋
ํ์ง๋ง ์ฃผ์ํ ์ ์ด ์๋ค.
๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ Thread A๊ฐ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๋์ค Thread B๊ฐ ์ด๋ฏธ ํ ๋น๋ ์ธ์คํด์ค๋ฅผ ์ ๊ทผํ ์ ์๋ค.
์ด ๊ฒฝ์ฐ, ์ธ์คํด์ค๊ฐ ์์ ํ ์ด๊ธฐํ๋๊ธฐ ์ ์ ์ฌ์ฉ๋๋ฏ๋ก ์ค๋์์ด ๋ฐ์ํ ์ ์๋ค.
ํ๋ฅ ์ ๋ฎ์ง๋ง, ์์ ์ฑ์ ์ํด ์ ์ ํ ์ฒ๋ฆฌ(์: volatile ์ ์ธ) ์์ด ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅ๋์ง ์๋๋ค.
public class LazyHolderSingleton {
private LazyHolderSingleton() {
}
// ์ค์ฒฉ(static) ํด๋์ค์ ์ธ์คํด์ค ์์ฑ
private static class Holder {
private static final LazyHolderSingleton INSTANCE = new LazyHolderSingleton();
}
// ์ธ๋ถ์์ ํธ์ถ ์ Holder ํด๋์ค๊ฐ ๋ก๋ฉ๋๋ฉด์ ์ธ์คํด์ค ์์ฑ
public static LazyHolderSingleton getInstance() {
return Holder.INSTANCE;
}
}
๊ฐ๋ฐ์๊ฐ ์ง์ ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ์ฝ๋๋ก ์ฒ๋ฆฌํ๋ ค๊ณ ํ๋ฉด, ํ๋ก๊ทธ๋จ ๊ตฌ์กฐ๊ฐ ๋ณต์กํด์ง๊ณ , ์ฑ๋ฅ ๋น์ฉ์ด ๋ฐ์ํ ์ ์์ผ๋ฉฐ, ํนํ ์ ํํ๊ฒ ๊ตฌํํ๊ธฐ ์ด๋ ค์ด ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
LazyHolder ๋ฐฉ์์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํผํ๊ณ , JVM์ ํด๋์ค ์ด๊ธฐํ ๊ณผ์ ์์ ๋ณด์ฅ๋๋ ์์์ ํน์ฑ์ ์ด์ฉํด ์ฑ๊ธํค ์ด๊ธฐํ ์ฑ ์์ JVM์๊ฒ ๋งก๊ธฐ๋ ๋ฐฉ๋ฒ์ด๋ค.
Singleton ํด๋์ค ์์ฒด์๋ LazyHolder ํด๋์ค์ ๋ณ์๊ฐ ์์ โ Singleton ํด๋์ค ๋ก๋ฉ ์์ ์์๋ Holder ํด๋์ค๊ฐ ์ด๊ธฐํ๋์ง ์์
getInstance() ๋ฉ์๋์์ Holder.INSTANCE๋ฅผ ์ฐธ์กฐํ๋ ์๊ฐ, Holder ํด๋์ค๊ฐ ๋ก๋ฉ๋๊ณ ์ธ์คํด์ค ์ด๊ธฐํ๊ฐ ์งํ๋จ
ํด๋์ค ๋ก๋ฉ๊ณผ ์ด๊ธฐํ ๊ณผ์ ์ JVM์ ์ํด ๋๊ธฐํ๊ฐ ๋ณด์ฅ๋๋ฏ๋ก, volatile์ด๋ synchronized ํค์๋๋ฅผ ์ฌ์ฉํ์ง ์์๋ ๋ฉํฐ์ค๋ ๋ ์์ ์ฑ์ ์ ์งํ๋ฉด์ ์ฑ๋ฅ๋ ์ฐ์ํ๋ค.
์๋ฐ์์ ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์ด ๋๋ฉด, ์ค๋ ๋๋ค์ด ํ ์์ญ๊ณผ ๋ฉ์๋(method) ์์ญ์ ๊ณต์ ํ๊ฒ ๋๋ค.
์ฆ, ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ธ ์ ์๊ธฐ ๋๋ฌธ์, ๊ณต์ ์์์ ๋ํ ๋๊ธฐํ๋ฅผ ๋ฐ๋์ ๊ณ ๋ คํด์ผ ํ๋ค.
synchronized๋ ๋ฝ(lock)์ ์ฌ์ฉํด ๋์ ์ ๊ทผ์ ์ ์ดํ๋ ํค์๋๋ค.
์ฃผ์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค ๊ฐ์ง๊ฐ ์๋ค.
1. synchronized method
๋ฉ์๋ ์ ์ธ๋ถ์ synchronized๋ฅผ ๋ถ์ด๋ฉด, ๋์ผ ์ธ์คํด์ค์ ๋ฉ์๋๋ผ๋ฆฌ๋ง lock์ ๊ณต์ ํ๋ค.
๋ค๋ฅธ ์ธ์คํด์ค์์๋ ์ํฅ์ ๋ฐ์ง ์์ผ๋ฏ๋ก, ์ธ์คํด์ค๋ณ๋ก ๋ ๋ฆฝ์ ์ธ ๋ฝ์ด ๊ฑธ๋ฆฐ๋ค.
2. static synchronized method
ํด๋์ค ๋จ์๋ก lock์ด ๊ฑธ๋ฆฌ๋ฉฐ, ์ธ์คํด์ค ๋ฝ๊ณผ๋ ๋ณ๋๋ก ๋์ํ๋ค.
ํด๋์ค ๋ฉ์๋ ๊ฐ์ ๋๊ธฐํ๋ ๋ณด์ฅ๋์ง๋ง, ์ธ์คํด์ค ๋ฉ์๋์๋ lock์ด ๊ณต์ ๋์ง ์๋๋ค.
3. synchronized block
ํ์ํ ์ฝ๋ ๋ธ๋ก๋ง ๋ฝ์ผ๋ก ๊ฐ์ธ์ ๋๊ธฐํ๋ฅผ ์ ์ดํ ์ ์๋ค.
๋ฐฉ๋ฒ 1: this๋ฅผ ์ฌ์ฉ โ ํ์ฌ ๊ฐ์ฒด๋ฅผ ๋ฝ์ผ๋ก ์ฌ์ฉ. ๋ค๋ฅธ block๋ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ๋๊ธฐํด์ผ ํจ
๋ฐฉ๋ฒ 2: ๋ณ๋ Object๋ฅผ ๋ฝ์ผ๋ก ์ฌ์ฉ โ ๋ธ๋ก๋ณ๋ก ๋ ๋ฆฝ์ ์ธ lock์ ๊ฑธ ์ ์์ด ํจ์จ์
4. static synchronized block
static synchronized method์ฒ๋ผ ํด๋์ค ๋จ์๋ก lock์ ๊ณต์
๋ค๋ง block ๋จ์๋ก lock ๋ฒ์๋ฅผ ์ง์ ํ ์ ์์ด, ์ธ๋ฐํ ์ ์ด๊ฐ ๊ฐ๋ฅํ๋ค
synchronized ๋ฉ์๋๊ฐ ๋ง์ผ๋ฉด, ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์๋ ๋ณ๋ชฉ ํ์์ด ๋ฐ์
์ฝ๊ฒ ๋งํด, ๋ฉํฐ ์ค๋ ๋์ฒ๋ผ ๋์ํ์ง๋ง ์ฌ์ค์ ์ฑ๊ธ ์ค๋ ๋์ฒ๋ผ ๋๋ ค์ง
๐ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ
-> LazyHolder ํจํด์ ์ฌ์ฉ
JVM์ ํด๋์ค ์ด๊ธฐํ ์์ ์์ ๋ณด์ฅ๋๋ thread-safe ํน์ฑ์ ์ด์ฉํด ์ด๊ธฐํ
๊ฐ๋ฐ์๊ฐ ์ง์ synchronized ์ฝ๋๋ฅผ ์์ฑํ์ง ์์๋ ๋จ โ ๋ณต์ก์ฑ ๊ฐ์
Holder ํด๋์ค์ ์ธ์คํด์ค๋ getInstance() ํธ์ถ ์์ ์๋ง ์์ฑ โ ๋ฉ๋ชจ๋ฆฌ ํจ์จ๋ ์ข์
์ฐธ๊ณ