동기화 synchronization

📁 ex01

☕ ATM.java

public class ATM {
    private int balance = 0;
    public void addMoney(int amount) {
        balance += amount;
    }
    public int getBalance() {
        return balance;
    }

    //  💡 앞에 synchronized를 붙이고 다시 실행해 볼 것
    public void withdraw (String name, int amount) {

        //  💡 또는 아래 내용을 이 블록으로 옮겨 볼 것
        //  - this는 현 쓰레드를 의미함
        //  - 메소드 내의 특정 작업만 동기화가 필요할 때
        //synchronized (this) {
        //}

        if (balance < amount) return;

        System.out.printf(
                "💰 %s 인출 요청 (현 잔액 %d)%n",
                name, balance
        );
        try {
            Thread.sleep(new Random().nextInt(700, 1000));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        balance -= amount;
        System.out.printf(
                "✅ %s 인출 완료 (현 잔액 %d)%n",
                name, balance
        );
    }
}

☕ CustomerRun.java

public class CustomerRun implements Runnable {
    String name;
    ATM atmToUse;
    int needed;

    public CustomerRun(String name, ATM atmToUse, int needed) {
        this.name = name;
        this.atmToUse = atmToUse;
        this.needed = needed;
    }
    @Override
    public void run() {
        while (atmToUse.getBalance() > needed) {
            atmToUse.withdraw(name, needed);

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

☕ Main.java

				ATM atm = new ATM();
        atm.addMoney(5000);

        Thread thr1 = new Thread(
                new CustomerRun("철수", atm, 500)
        );
        Thread thr2 = new Thread(
                new CustomerRun("영희", atm, 300)
        );
        Thread thr3 = new Thread(
                new CustomerRun("돌준", atm, 400)
        );

        thr1.start();
        thr2.start();
        thr3.start();

캐싱에 의한 문제 방지하기

📁 ex02

☕ Cache1.java

public class Cache1 {

    static boolean stop = false;
    public static void main(String[] args) {
        new Thread(() -> {
            int i = 0;
            while (!stop) {
                i++;

                // ⭐️ 아래를 주석처리하고 다시 실행해보기
                System.out.println(i);
            }

            System.out.println("- - - 쓰레드 종료 - - -");
        }).start();

        try { Thread.sleep(1000);
        } catch (InterruptedException e) {}

        stop = true;

        //  💡 JVM의 캐시 방식에 따라 멈출 수도 안 멈출 수도 있음
        //  - stop으로의 접근이 동기화되지 않았을 시
        //  - 한 쓰레드가 그 값을 바꿔도 다른 쓰레드는 캐시에 저장된
        //  - 바뀌기 이전 값을 참조할 수 있음
        //    - println 메소드는 위 코드에서 캐시를 비우는 이유 제공
    }
}