๊ณต์ ์์๊ณผ ์๊ณ์์ญ
๐ฒ ๊ณต์ ์์ : ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ ๊ทผํ ์ ์๋ ์์
๐ฒ ์๊ณ ์์ญ : ๊ณต์ ์์๋ค ์ค ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ์ ๊ทผํ์ ๋, ๋ฌธ์ ๊ฐ ์๊ธธ๋งํ ๋ถ๋ถ
๐ถ๊ฒฝ์ ์ํ?
๋ ์ด์์ ์ค๋ ๋๊ฐ ๊ณต์ ์์์ ๋ณํ์ ์ผ๋ก ์ฝ๊ฑฐ๋ ์ธ๋, ํ์ด๋ฐ์ด๋ ์ ๊ทผ ์์์ ๋ฐ๋ผ ์คํ ์์๊ฐ ๋ฌ๋ผ์ง๋ ์ํฉ(race ํ์)
๐ถRead - Modify - Write
@RestController
@RequestMapping("/race-condition")
public class RaceConditionCotroller {
public static Integer studentCount = 0;
@PostMapping("/1/increase")
public ResponseEntity<Void> increaseCount() {
studentCount++;
return ResponseEntity.ok().build();
}
@GetMapping("/1/count")
public ResponseEntity<Integer> getCount() {
return ResponseEntity.ok(studentCount);
}
}
๋ง์ฝ ์ฌ๊ธฐ์ 30๊ฐ์ ์์ฒญ์ด ๋ค์ด์ increase ์ฐ์ฐ์ ์ํํ๋ค๊ณ ํด๋ณด์. ์์ํ ์ ์๋ ๊ฒฐ๊ณผ๋ก studentCount๋ 30์ด ๋ ๊ฒ์ด๋ค. ํ์ง๋ง, ์ค์ ๋์์ ๊ทธ๋ฌ์ง ์์ ์ ์๋ค.
์ด๋ studentCount++ ๋ผ๋ ์ฝ๋์์ Thread๊ฐ์ race ๋๋ฌธ์ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ด๋ค. studentCount๋ ๊ทธ ๋ด๋ถ์ ์ผ๋ก ๋ณ์์ ๊ฐ์ ์ฝ๊ณ (Read), ์์ ํ๊ณ (Modify), ๋ฎ์ด์์ฐ๋(Write)์ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
ํ๋ฐ, ๋ค๋ฅธ ์ฐ๋ ๋์์ ์ด๋ฌํ ์ธ ๊ณผ์ ์์ ์์๊ฐ ์๋ชป ๊ฒน์น๋ค๋ฉด, ๋ฎ์ฌ์์์ง ๋ ํ๋๋ฅผ ๋ํ๋ ๊ณผ์ ์ด ๋ฌด์๋ ์ ์๋ค.
๐ถCheck - then - act
@PostMapping("/2/check-then-act")
public ResponseEntity<Void> printWarning() throws InterruptedException {
studentCount++;
if (studentCount < 30) {
Thread.sleep(1);
System.out.println("ํ๊ฐ์ํ! studentCount = " + studentCount);
}
return ResponseEntity.ok().build();
}
์ ์์๋ ์๊ฐ ์ ์ฒญ ํ์, ์๋ฅผ ์ธ์ 30๋ช ๋ฏธ๋ง์ด๋ฉด ํ๊ฐ ์ํ์ด๋ผ๋ ์ถ๋ ฅ๋ฌธ์ ํ์ํ๋ค. ํ์ง๋ง ๊ทธ ๊ฒฐ๊ณผ ์ถ๋ ฅ๋ฌธ์๋ ์์๊ณผ ๋ค๋ฅธ ์๋ฑํ ์ซ์๊ฐ ์จ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด ์ด์ ๋ํ Thread๊ฐ์ race ๋๋ฌธ์ด๋ค. ์๋ฅผ ๋ค์ด, Thread1์์ studentCount๊ฐ ํ๋ ์ฆ๊ฐํ๊ณ , if๋ฌธ์ ํต๊ณผํ๋ค๊ณ ํ์. ๊ทธ๋ฌ๋ฉด, Thread2์์ ํ๋๋ฅผ ์ฆ๊ฐ์์ผ 30๋ณด๋ค ์ปค์ง๋๋ผ๋ Thread1์์์ if๋ฌธ ๋ก์ง์ ๊ทธ๋๋ก ์๋ํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
์์์ฑ
๐ฒ ์์์ฑ : ๊ณต์ ์์์ ๋ํ ์์ ์ ๋จ์๊ฐ ๋์ด์ ์ชผ๊ฐ์ง ์ ์๋ ํ๋์ ์ฐ์ฐ์ธ ๊ฒ์ฒ๋ผ ๋์ํ๋ ๊ฒ์ด๋ค.
์์ ๊ฐ์ ๊ฒฝ์ ์กฐ๊ฑด์ ๋ฐ์์ํค์ง ์๊ฒ ํ๊ธฐ ์ํด ์์์ฑ์ ๋ณด์ฅํ์ฌ์ผ ํ๋ค.
๊ฐ์์ฑ
public class Main {
public static boolean missileLaunched = false;
public static class MissileIntercepter extends Thread {
@Override
public void run() {
while(!missileLaunched) { /* ๋๊ธฐ */ }
System.out.println("์๊ฒฉ!");
}
}
public static void main(String[] args) throws InterruptedException{
final MissileIntercepter missileIntercepter = new MissileIntercepter();
missileIntercepter.start();
Thread.sleep(5000);
launchMissile();
missileIntercepter.join();
}
private static void launchMissile() { missileLaunched = true; }
}
Thread ์์์ ๋ณ์์ ๊ฐ์ด ๋ฐ๋ ๋๊น์ง waitํ๋ ์ฝ๋ ์์ ์ด๋ค. ํ์ง๋ง, ์์๊ณผ ๋ค๋ฅด๊ฒ while๋ฌธ์ ๋น ์ ธ๋๊ฐ์ง ๋ชปํด, ์ถ๋ ฅ์ ํ์ง ๋ชปํ๊ณ ๋ฌดํ ๋ฃจํ์ ๋น ์ง๊ฒ ๋๋ค.
Thread์์ ๊ฐ์ง๋ ๋ณ์ ๊ฐ๋ค์ ๊ฒฐ๊ตญ Main Memory์์ ๊ฐ์ ธ์จ๋ค. ํ์ง๋ง, ์ด Main Memory์ ๊ฐ์ 1:1๋ก ๋ฐ๋ก ๋ฐ์๋๋ ๊ฒ์ด ์๋๋ผ ๊ฐ Thread๋ง๋ค CPU Cache์์ ๊ฐ์ด ์ ์ฅ๋๋ค.
๊ทธ๋์ ์ค๊ฐ์ Main Thread์์ missile ๋ณ์์ ๊ฐ์ด ๋ณํ์ฌ Main Memory ์์ ๋ฐ์๋์ด๋, Thread1 ์์์๋ ๋ฐ์๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ฌดํ ๋ฃจํ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ, Main Memory ์์ ์๋ ์ง์ง ๊ฐ์ ๋ณด๋ ๊ฒ์ "๊ฐ์์ฑ" ์ด๋ผ๊ณ ํ๋ค.
๊ฐ์์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด์ ์๋ฐ์์๋ volatile์ด๋ผ๋๊ฒ์ ์ ๊ณตํ๋ค. ์ฆ, Main Memory ์์ ์ง์ง ๋ณ์ ๊ฐ์ ๋ณผ ์ ์๋๋ก ํ๋ค.
public class Main {
public volatile static boolean missileLaunched = false;
public static class MissileIntercepter extends Thread {
@Override
public void run() {
while(!missileLaunched) { /* ๋๊ธฐ */ }
System.out.println("์๊ฒฉ!");
}
}
public static void main(String[] args) throws InterruptedException{
final MissileIntercepter missileIntercepter = new MissileIntercepter();
missileIntercepter.start();
Thread.sleep(5000);
launchMissile();
missileIntercepter.join();
}
private static void launchMissile() { missileLaunched = true; }
}
๋๊ธฐํ
๐ฒ ๋๊ธฐํ๋?
์์์ฑ๊ณผ ๊ฐ์์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํ ๊ฒ์ด์, ํน์ ์ค๋ ๋๊ฐ ์์ ์ ์ํํ๋ ๋์ ๋ค๋ฅธ ์์ ์ ์งํํ์ง ์๊ณ ๋๊ธฐํ๋ ๋ฐฉ์์ ๋งํ๋ค.
๋ธ๋กํน๊ณผ ๋ชจ๋ํฐ
๐ฒ ๋ชจ๋ํฐ๋?
์๋ฐ์์ ๋๊ธฐํ๋ฅผ ์ํ ๋๊ตฌ์ด๋ค.
โ ๋ฐฐํ ๋๊ธฐ๋ synchronized
โ ์กฐ๊ฑด ๋๊ธฐ๋ wait(), notify(), notifyAll()์ด๋ค.
์๊ณ ์์ญ์๋ ํ๋์ ์ฐ๋ ๋๋ง์ด Lock์ ๊ฐ์ง๊ณ ๋ค์ด๊ฐ ์ ์๋ค.
๋ง์ฝ ์๊ณ ์์ญ์์ ์ฐ๋ ๋๊ฐ wait() ํจ์๋ฅผ ํตํด sleepํ๋ค๋ฉด, ์กฐ๊ฑด๋๊ธฐ ํ๋ก ๋ค์ด๊ฐ๊ฒ ๋๋ค.
๊ทธ๋ฌ๋ฉด, ์๊ณ ์์ญ์ด ๋น๊ฒ๋๊ณ , ๋ฐฐํ๋๊ธฐํ์ ์์ฌ์๋ ์ฐ๋ ๋๊ฐ ์๊ณ ์์ญ์์ผ๋ก ์ด๋ํ๋ค.
๋ง์ฝ ์ด ์ํ์์ notify()๋ฅผ ํธ์ถํ๋ค๋ฉด, ์กฐ๊ฑด๋๊ธฐ ํ์์ ์ ๋ค์ด ์๋ ์ฐ๋ ๋๊ฐ ๊นจ์ด๋ ๋ค์ ์๊ณ ์์ญ์ด ๋น ์๊ฐ ์๊ณ ์์ญ์ผ๋ก ๋ค์ด๊ฐ๋ค.
์๋ฐ์์ ๋ด๋ถ์ ์ผ๋ก ์ด๋ฌํ ๋์์ด ๊ฐ๋ฅํ๋๋ก ํ๋ ค๋ฉด(์ฆ, ๋ธ๋กํน์ด ๊ฐ๋ฅํ๊ฒ ํ๋ ค๋ฉด) Synchronized ํค์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค. ์๋๋ ์์ ์์์ฑ์ด ๋ณด์ฅ ์๋ ์ฝ๋๋ฅผ ์์ ํ ๊ฒ์ด๋ค.
@PostMapping("/2/check-then-act")
public synchronized ResponseEntity<Void> printWarning() throws InterruptedException {
studentCount++;
if (studentCount < 30) {
Thread.sleep(1);
System.out.println("ํ๊ฐ์ํ! studentCount = " + studentCount);
}
return ResponseEntity.ok().build();
}
synchronized์ ๋์๋ฐฉ์
์๊ณ ๊ตฌ์ญ ์์๋ ํ๋์ ์ฐ๋ ๋๋ง์ด ๋ค์ด๊ฐ ์ ์๊ณ , ๊ฒฐ๋ก ์ ์ผ๋ก๋ ๋๊ธฐ์ ์ผ๋ก ์ฐ๋ ๋๊ฐ ๋์ํ์ฌ ์ด์ ๊ณผ ๊ฐ์ race ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์๊ณ , ๊ฐ์์ฑ ๋ํ ๋ณด์ฅ๋๋ค.
๋ฌผ๋ก synchronized๋ฅผ ๋ถ์ธ๋ค๊ณ ๋ฌด์ ์ด ๋๋ ๊ฒ์ ์๋๋ค.
ํ๋์ ์๊ณ ๊ตฌ์ญ์์์๋ง ์ฐ๋ ๋๊ฐ ๋์ํ๊ธฐ ๋๋ฌธ์ ๋๋จธ์ง ์ฐ๋ ๋๋ค์ ๋๊ธฐํด์ผ ํ๋ ์ฑ๋ฅ ์ ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ธ๋กํน - ๋ฐ๋๋ฝ(๊ต์ฐฉ์ํ)
๋ฐ๋๋ฝ์ ๋ ๊ฐ์ ์ฐ๋ ๋๊ฐ ์๋ก๊ฐ ๊ฐ์ง๊ณ ์๋ ๋ฝ์ด ํด์ ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ๋ฅผ ๋งํ๋ค.
์ฆ, ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ๋์์๋ ์๋ฌด๋ฐ ์์ ์ ํ์ง ๋ชปํ๊ณ ๋ฌดํ์ ๋๊ธฐํด์ผ๋ง ํ๋ค.
public class Main {
public static Object object1 = new Object();
public static Object object2 = new Object();
public static void main(String[] args) {
FirstThread thread1 = new FirstThread();
SecondThread thread2 = new SecondThread();
thread1.start();
thread2.start();
}
private static class FirstThread extends Thread{
@Override
public void run() {
synchronized (object1){
System.out.println("First Thread has object1's lock");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("First Thread want to have object2's lock. so wait");
synchronized (object2){
System.out.println("First Thread has object2's lock too");
}
}
}
}
private static class SecondThread extends Thread{
@Override
public void run() {
synchronized (object2){
System.out.println("Second Thread has object2's lock");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Second Thread want to have object1's lock, so wait");
synchronized (object1){
System.out.println("Second Thread has object1's lock too");
}
}
}
}
}
์์ฒ๋ผ, object1์ด ๋ฝ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, object2์ ๋ํ ๋ฝ์ ์ํ๋ค.
๋ํ object2๋ํ ๋ฝ์ ๊ฐ์ง๊ณ ์๊ณ , object1์ ๋ํ ๋ฝ์ ์ํ๋ค. ์ฆ, ์๋ก ์๋๋ฐฉ์ด ์ ์ ํ ์์์ ์ํ์ฌ ๋๊ธฐํ๋ ์ํ์ด๊ธฐ ๋๋ฌธ์ ๋ฌดํ์ ๊ธฐ๋ค๋ฆด ์ ๋ฐ์ ์๋ ๊ฒ์ด๋ค.
๋ ผ๋ธ๋กํน
๐ฒ ๋ ผ๋ธ๋กํน์ด๋?
๋ค๋ฅธ ์ฐ๋ ๋์ ์์ ์ฌ๋ถ์๋ ์๊ด์์ด ์์ ์ ์์ ์ ์ํํ๋ ๋ฐฉ์
๐ฒ CAS
์์๊ฐ, ๊ธฐ๋๊ฐ, ์๋ก์ด ๊ฐ(์ฐ์ฐ๊ฒฐ๊ณผ)์ ๋งํ๋ฉฐ, ์์ ๊ฐ๊ณผ ๊ธฐ๋๊ฐ์ด ๊ฐ๋ค๋ฉด ์์ ๊ฐ์ ์๋ก์ด ๊ฐ์ผ๋ก ์์ ํ๊ณ True๋ฐํ, ๋ค๋ฅด๋ค๋ฉด ์์ ํ์ง ์๊ณ False ๋ฐํ
Atomic ํ์
๐ฒ ๋์์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด์ ์๋ฐ์์ ์ ๊ณตํ๋ Wrapper class์ด๋ค.
CAS(Compare and Set) + Volatile
๐ถ AtomicReference
๋ ผ๋ธ๋กํน์ AtomicReference๋ฅผ ํตํด์ ๋์์ฑ์ ๋ณด์ฅํ ์ ์๋ค.
AtomicReference์ ๋ด๋ถ value ๊ฐ์ ํ์ ์ ์ดํด๋ณด๋ฉด, volatile์ด๋ผ๋ ํค์๋๊ฐ ๋ถ์ด์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์์ ๋งํ๋ฏ์ด volatile์ Main Memory ์์ ์ง์ง ๊ฐ์ ๋ณผ ์ ์๋๋ก ํ๋ ๊ฒ์ด๋ผ๊ณ ํ์๋ค.
์ฆ, JVM ์์์์ ํ์ฌ ๋ณ์ ๊ฐ์ ๊ฐ ์ฐ๋ ๋์ CPU ์บ์์์์ ๊ฐ๊ณผ ์ผ์น์ํฌ ์ ์๊ณ , ์ฐ์ฐ์ ํ ์ ์๋ค.
์ฐ์ฐ์ ํ๋ ๊ฒฝ์ฐ compareAndSet()์ด๋ผ๋ ํจ์๊ฐ ์คํ๋๋๋ฐ, ์ด๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋ ์์ ๊ฐ๊ณผ ์ฐ๋ ๋ ๋ด๋ถ์ ๊ธฐ๋ ๊ฐ์ ๋น๊ตํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ผ์นํ๋ค๋ฉด True๋ฅผ ๋ฆฌํดํ๊ณ ์ผ์นํ์ง ์์ผ๋ฉด False๋ฅผ ๋ฆฌํดํ๋ ์์ผ๋ก ๋์ํ๋ค.
์ฆ, volatile์ ํตํด์ Main Memory ์์์ ์ง์ง ๊ฐ์ ํ์ธํ๋ ๊ฐ์์ฑ์ ์งํฌ ์ ์์ผ๋ฉฐ, CAS๋ฅผ ํตํด์ ์ฐ์ฐ ์๋ง๋ค ๋น๊ต ์๊ณ ๋ฆฌ์ฆ์ ์ ์ฉํด ์์์ฑ์ ๋ณด์ฅํ ์ ์๋ค.
@RestController
@RequestMapping("/atomic")
public class AtomicController {
private AtomicInteger studentCount = new AtomicInteger(0);
@PostMapping("/1/increase")
public ResponseEntity<Void> increaseCount() {
studentCount.addAndGet(1);
return ResponseEntity.ok().build();
}
@GetMapping("/1/count")
public ResponseEntity<AtomicInteger> getCount() {
return ResponseEntity.ok(studentCount);
}
}
์์ ๋ณด์๋, student๋ฅผ Countํ๋ ์์ ์์ AtomicInteger๋ก ๋ ผ๋ธ๋กํนํ ์ํฉ์์ ๊ฐ์์ฑ๊ณผ ์์์ฑ์ ์ง์ผ์ฃผ์๋ค.
Thread ์์ ํ ๊ฐ์ฒด
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ค๋ ์ํฉ์์ ํด๋์ค์ ๋ด๋ถ ๊ฐ์ ์์ ์ ์ธ ์ํ๋ก ์ ์งํ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ๊ฐ์ฒด๋ฅผ ์ค๊ณํ๊ธฐ ์ํด์๋ ์ ๋ง ๋ง์ ๋ฐฉ๋ฒ์ด ์๋ค.(์ค๋ ๋ ํ์ , ๋ฝ, ์๋ฐ ๋ชจ๋ํฐ ํจํด ๋ฑ๋ฑ...)
ํ์ง๋ง, ๊ฐ์ฅ ํ์คํ ๋ฐฉ๋ฒ์ ๊ณต์ ๋ณ์๋ฅผ ์ต์ํํ๊ณ ์บก์ํํ๋ฉฐ ๋ฌธ์ํ๋ฅผ ์ํ๋ ๊ฒ์ด๋ค.
References
https://math-coding.tistory.com/175
https://www.youtube.com/watch?v=ktWcieiNzKs&list=PL-sctLNt2NB8l7wq5Zh3pAELzL2jFjy_Q&index=2&t=901s
'๐ซ Language > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java] ๋ชจ๋ ๊ฐ์ฒด๋ ๋ถ๋ณ ๊ฐ์ฒด์ฌ์ผ ํ ๊น? (0) | 2024.02.28 |
---|---|
[Java] ๊ฐ์ฒด ์งํฅ ์ค๊ณ 5์์น(SOLID)์ด๋? (2) | 2024.01.13 |
[Java] ์ผ๊ธ ์ปฌ๋ ์ ๊ณผ ์ฌ์ฉํด์ผ ํ๋ ์ด์ ์ ๋ํ์ฌ (2) | 2024.01.13 |
[Java] JUnit5 ์ฌ์ฉ๋ฒ ์์๋ณด๊ธฐ (0) | 2024.01.09 |
[Java] ๋๊ธฐ/๋น๋๊ธฐ & ๋ธ๋ญํน/๋ ผ๋ธ๋ญํน์ด๋? (0) | 2024.01.02 |