synchronized 동기화
`synchronized`는 Java에서 멀티스레딩 환경에서 동기화(synchronization)를 위해 사용하는 키워드입니다. 이는 특정 코드 블록이나 메서드가 한 번에 하나의 스레드만 접근할 수 있도록 제한하여, 여러 스레드가 동시에 접근할 때 발생할 수 있는 데이터 불일치 문제를 방지합니다.
### 기본 사용법
`synchronized`는 주로 두 가지 방식으로 사용됩니다: 블록 수준 동기화와 메서드 수준 동기화입니다.
1. **블록 수준 동기화**:
특정 객체를 대상으로 동기화 블록을 정의합니다. 이 경우 해당 블록 내의 코드가 실행될 때, 지정된 객체에 대한 잠금을 획득한 스레드만 접근할 수 있습니다.
```java
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
synchronized (lock) {
return count;
}
}
}
```
2. **메서드 수준 동기화**:
메서드 자체에 `synchronized` 키워드를 붙여서 해당 메서드가 호출될 때 객체에 대한 잠금을 획득하도록 합니다.
```java
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
```
### 동작 원리
`synchronized` 키워드를 사용하면 다음과 같은 방식으로 동작합니다:
1. **잠금 획득**:
스레드가 `synchronized` 블록이나 메서드에 들어가면, 해당 객체의 잠금을 획득합니다. 다른 스레드가 동일한 객체의 `synchronized` 블록이나 메서드에 접근하려고 하면, 잠금이 해제될 때까지 대기합니다.
2. **코드 실행**:
잠금을 획득한 스레드는 `synchronized` 블록이나 메서드 내부의 코드를 실행합니다.
3. **잠금 해제**:
블록이나 메서드 실행이 끝나면 잠금을 해제하여 다른 대기 중인 스레드가 접근할 수 있도록 합니다.
### 주의사항
- **성능 저하**: 동기화는 멀티스레딩 환경에서 데이터 불일치 문제를 해결하는데 유용하지만, 잠금을 획득하고 해제하는 오버헤드가 있기 때문에 성능 저하를 초래할 수 있습니다.
- **데드락(교착 상태)**: 두 개 이상의 스레드가 서로 상대방의 잠금을 기다리며 무한 대기 상태에 빠지는 데드락 상황을 주의해야 합니다.
- **필요한 경우에만 사용**: 동기화가 필요한 최소한의 코드 블록에만 `synchronized`를 적용하여 성능에 미치는 영향을 최소화해야 합니다.
### 예제
다음은 `synchronized`를 사용하여 두 개의 스레드가 안전하게 카운터 값을 증가시키는 예제입니다.
```java
public class SynchronizedExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + counter.getCount());
}
}
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
```
이 예제에서는 두 개의 스레드가 `Counter` 객체의 `increment` 메서드를 동시에 호출하더라도, `synchronized` 키워드 덕분에 `count` 변수가 안전하게 증가합니다.