## Java에서 스레드 안전성(Thread-Safety)의 이해와 구현

멀티스레드 환경에서 프로그램의 안정성과 정확성을 보장하기 위해 '스레드 안전성(Thread-Safety)' 개념은 필수적입니다. 스레드 안전성은 여러 스레드가 동시에 특정 코드나 데이터에 접근하더라도 예상치 못한 오류나 데이터 손상이 발생하지 않도록 보장하는 것을 의미합니다. 이를 제대로 이해하고 구현하지 못하면 '경쟁 조건(Race Condition)'과 같은 심각한 문제가 발생할 수 있습니다.

### 1. 공유 상태(Shared State)와 지역 변수(Local Variable)

스레드 안전성 문제의 핵심은 여러 스레드가 공유하는 '가변 상태(Mutable Shared State)'에 있습니다. 각 스레드마다 독립적으로 생성되고 관리되는 지역 변수는 본질적으로 스레드 안전합니다. 하지만 여러 스레드가 동시에 접근하여 수정할 수 있는 클래스의 멤버 변수와 같은 공유 상태는 반드시 동기화 메커니즘을 통해 보호되어야 합니다.

public class ThreadSafeExample {

    // 여러 스레드가 공유하는 상태(Shared State)입니다.
    // 동기화 없이 동시에 접근하면 '경쟁 조건(Race Condition)'이 발생할 수 있습니다.
    private int sharedNumber = 0;

    public void demonstrateThreadSafetyIssues() {

        // 이 부분은 Thread-Safe 하지 않습니다.
        // 'sharedNumber'는 공유 멤버변수이므로, 여러 스레드가 동시에 읽고 수정할 때 동기화가 필요합니다.
        this.sharedNumber = this.sharedNumber + 1;
        this.sharedNumber++;

        // 이 부분은 Thread-Safe 합니다.
        // 'localNumber'는 각 스레드마다 독립적으로 생성되는 지역 변수이므로 동기화가 필요 없습니다.
        int localNumber = 0;
        localNumber++;

        // 이 부분은 Thread-Safe 하지 않습니다.
        // 'modifySharedState()' 메서드가 공유 멤버변수를 변경하기 때문입니다.
        modifySharedState();

        // 이 부분은 Thread-Safe 합니다.
        // 'useLocalVariablesOnly()' 메서드는 지역변수만 사용하므로 여러 스레드가 동시에 호출해도 안전합니다.
        useLocalVariablesOnly();
    }

    // Thread-Safe 하지 않은 메서드입니다.
    // 공유 멤버변수 'sharedNumber'를 변경합니다.
    public int modifySharedState() {
        this.sharedNumber++;
        return this.sharedNumber;
    }

    // Thread-Safe 한 메서드입니다.
    // 지역변수만 사용하며 공유 상태를 변경하지 않습니다.
    public int useLocalVariablesOnly() {
        int number = 0;
        number++;
        return number;
    }

    // synchronized 키워드를 사용하여 메서드를 Thread-Safe 하게 만듭니다.
    // 메서드 전체에 락(Lock)을 걸어 한 번에 하나의 스레드만 접근하도록 보장합니다.
    public synchronized int synchronizedMethod() {
        sharedNumber++;
        return sharedNumber;
    }

    // synchronized 블록을 사용하여 코드의 특정 구간만 Thread-Safe 하게 만듭니다.
    // 'this' 객체를 락으로 사용하여 동기화된 코드 블록 내에서만 공유 상태를 변경합니다.
    public int synchronizedBlock() {
        synchronized (this) {
            sharedNumber++;
            return sharedNumber;
        }
    }
}


### 2. 스레드 안전성 확보 방법

Java에서는 `synchronized` 키워드를 사용하여 스레드 안전성을 확보하는 가장 기본적인 방법을 제공합니다.

* **`synchronized` 메서드:** 메서드 전체에 락을 걸어 한 번에 하나의 스레드만 해당 메서드를 실행할 수 있도록 보장합니다.
* **`synchronized` 블록:** 특정 코드 블록에만 락을 걸어, 해당 블록 내의 공유 자원에 대한 접근을 동기화합니다. 이는 메서드 전체에 락을 거는 것보다 더 세밀한 제어가 가능하며, 성능상의 이점을 제공할 수 있습니다.

위 예제에서 `synchronizedMethod()`는 메서드 레벨에서, `synchronizedBlock()`은 블록 레벨에서 `sharedNumber` 변수에 대한 접근을 안전하게 보호하는 방법을 보여줍니다. 이를 통해 여러 스레드가 동시에 `sharedNumber`를 증가시키더라도 항상 올바른 최종 값을 얻을 수 있게 됩니다.

### 3. 추가적인 스레드 안전성 고려 사항

`synchronized` 외에도 Java에서는 `java.util.concurrent.atomic` 패키지의 `AtomicInteger`, `AtomicLong` 등과 같은 원자(Atomic) 변수, `java.util.concurrent.locks` 패키지의 `Lock` 인터페이스 및 구현체(`ReentrantLock`), 그리고 `ConcurrentHashMap`과 같은 동시성 컬렉션(Concurrent Collections)을 통해 더욱 다양한 스레드 안전성 기법을 제공합니다. 상황과 요구사항에 맞춰 적절한 동기화 메커니즘을 선택하고 적용하는 것이 중요합니다.