자바 및 spring boot

synchronized 동기화

dani717 2024. 6. 2. 16:01

`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` 변수가 안전하게 증가합니다.