### 자바 스레드 동기화에 대한 설명 스레드 동기화는 멀티스레드 환경에서 여러 스레드가 공유 자원(예: 변수, 객체 등)에 동시에 접근할 때 발생할 수 있는 데이터 불일치 문제를 해결하기 위해 사용됩니다. 자바에서는 동기화를 통해 특정 코드 블록이나 메서드가 한 번에 하나의 스레드만 실행하도록 보장합니다. 이를 통해 **경쟁 조건(race condition)**을 방지하고, 데이터의 무결성을 유지할 수 있습니다. 자바에서 스레드 동기화를 구현하는 주요 방법은 `synchronized` 키워드와 `java.util.concurrent` 패키지의 도구(예: `Lock`, `Semaphore` 등)를 사용하는 것입니다. 아래에서는 `synchronized` 키워드를 중심으로 설명하고, 예시를 제공하겠습니다. --- ### 스레드 동기화의 필요성 예를 들어, 은행 계좌 잔액을 관리하는 프로그램에서 두 스레드가 동시에 잔액을 수정하려고 하면 문제가 발생할 수 있습니다. 동기화가 없으면 잔액이 잘못 계산될 가능성이 높습니다. 이를 **경쟁 조건**이라고 부르며, 동기화로 해결할 수 있습니다. --- ### `synchronized` 키워드 사용 방법 자바에서 동기화는 두 가지 방식으로 적용됩니다: 1. **`synchronized` 메서드**: 메서드 전체를 동기화하여 한 번에 하나의 스레드만 실행하도록 만듭니다. 2. **`synchronized` 블록**: 특정 코드 블록만 동기화하여 더 세밀한 제어가 가능합니다. --- ### 예시 1: 동기화 없는 경우 (문제 발생) 아래는 동기화 없이 두 스레드가 계좌 잔액을 동시에 수정하는 예시입니다. ```java class BankAccount { private int balance = 1000; // 초기 잔액 public void withdraw(int amount) { if (balance >= amount) { System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount); balance -= amount; // 잔액 감소 System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance); } } public int getBalance() { return balance; } } public class NoSyncExample { public static void main(String[] args) { BankAccount account = new BankAccount(); Thread t1 = new Thread(() -> { account.withdraw(800); }, "스레드1"); Thread t2 = new Thread(() -> { account.withdraw(800); }, "스레드2"); t1.start(); t2.start(); } } ``` - **출력 예시 (무작위 결과)**: ``` 스레드1 출금 시도: 800 스레드2 출금 시도: 800 스레드1 출금 후 잔액: 200 스레드2 출금 후 잔액: -600 ``` - **문제**: 두 스레드가 동시에 `withdraw`를 호출하면서 잔액이 음수가 되는 비정상적인 결과 발생. 이는 `if (balance >= amount)` 조건을 확인한 후 `balance -= amount`가 실행되기 전에 다른 스레드가 끼어들었기 때문입니다. --- ### 예시 2: `synchronized` 메서드로 동기화 `withdraw` 메서드에 `synchronized`를 적용하면 한 번에 하나의 스레드만 메서드를 실행할 수 있습니다. ```java class BankAccount { private int balance = 1000; public synchronized void withdraw(int amount) { if (balance >= amount) { System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount); try { Thread.sleep(100); // 경쟁 조건 시뮬레이션 } catch (InterruptedException e) { e.printStackTrace(); } balance -= amount; System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance); } else { System.out.println(Thread.currentThread().getName() + " 잔액 부족"); } } public int getBalance() { return balance; } } public class SyncMethodExample { public static void main(String[] args) { BankAccount account = new BankAccount(); Thread t1 = new Thread(() -> { account.withdraw(800); }, "스레드1"); Thread t2 = new Thread(() -> { account.withdraw(800); }, "스레드2"); t1.start(); t2.start(); } } ``` - **출력 예시**: ``` 스레드1 출금 시도: 800 스레드1 출금 후 잔액: 200 스레드2 출금 시도: 800 스레드2 잔액 부족 ``` - **설명**: `synchronized` 덕분에 첫 번째 스레드가 `withdraw`를 완료할 때까지 두 번째 스레드가 대기하며, 잔액이 음수로 떨어지지 않음. --- ### 예시 3: `synchronized` 블록으로 동기화 메서드 전체가 아닌 특정 코드 블록만 동기화하려면 `synchronized` 블록을 사용합니다. 이때 동기화 대상 객체(락 객체)를 지정해야 합니다. ```java class BankAccount { private int balance = 1000; private final Object lock = new Object(); // 락 객체 public void withdraw(int amount) { System.out.println(Thread.currentThread().getName() + " 출금 시도: " + amount); synchronized (lock) { // 동기화 블록 if (balance >= amount) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } balance -= amount; System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance); } else { System.out.println(Thread.currentThread().getName() + " 잔액 부족"); } } } public int getBalance() { return balance; } } public class SyncBlockExample { public static void main(String[] args) { BankAccount account = new BankAccount(); Thread t1 = new Thread(() -> { account.withdraw(800); }, "스레드1"); Thread t2 = new Thread(() -> { account.withdraw(800); }, "스레드2"); t1.start(); t2.start(); } } ``` - **출력 예시**: ``` 스레드1 출금 시도: 800 스레드2 출금 시도: 800 스레드1 출금 후 잔액: 200 스레드2 잔액 부족 ``` - **설명**: `synchronized (lock)` 블록 내의 코드만 동기화되며, `lock` 객체를 통해 스레드 간 접근을 제어함. 메서드 전체를 동기화하는 것보다 유연성이 높음. --- ### 동기화의 장점과 주의점 - **장점**: - 공유 자원에 대한 안전한 접근 보장. - 데이터 무결성 유지. - **주의점**: - 과도한 동기화는 성능 저하(데드락, 스레드 대기 시간 증가)를 유발할 수 있음. - 필요한 범위만 동기화하도록 설계해야 함. --- ### 결론 스레드 동기화는 멀티스레드 프로그래밍에서 필수적인 개념으로, 자바에서는 `synchronized` 키워드를 통해 간단히 구현할 수 있습니다. `synchronized` 메서드와 블록을 적절히 사용하면 경쟁 조건을 방지하고 안정적인 프로그램을 작성할 수 있습니다. 위 예시를 참고하여 실제 애플리케이션에서 동기화를 적용해 보세요!