카테고리 없음

[JAVA] Thread safe

구로모논 2025. 4. 14. 17:00

1. 개념

Thread safe 란, 멀티 스레드 프로그래밍에서 여러 쓰레드로부터 동시에 접근하더라도 프로그램 실행에는 영향이 없는 것을 의미한다.

2. 왜 사용할까?

위에서 말했듯, 여러 쓰레드가 동시에 접근하더라도, 프로그램 실행에는 영향이 없어야 한다.
영향을 주는 경우의 예시는, 쉽게는 데이터 변경이 일어나는 작업을 예로 들 수 있겠다.

A라는 데이터를 1번 쓰레드가 변경을 시도하려했는데, 2번 쓰레드에서도 동시에 요청이 들어와서 변경을 시도하려한다.
1번 쓰레드가 데이터 변경을 처리할때, 2번 쓰레드가 같은 시도를 하게된다면 어떻게 될까?

1번에서 조회 후 변경작업을 한다고 치자, 조회 조건과 변경해야할 데이터가 같은 컬럼이라면, 2번에서는 조회할 데이터를 찾지 못 할수도 있다.
혹은, 1번의 작업이 모두 끝나지 않은 상태에서 2번의 작업은 모두 완료되어 데이터 변경까지 이루어졌다면, 1번이 조회, 변경작업의 대상 자체가 달라져버리거나 없어지는 경우가 생길 수 있다.
이런 경쟁 상태를 만들어서 후발 요청의 처리는 할 수 없게 만들었고, 해당 요청의 결과는 실패로 이어지거나 오류를 발생시킬 수 있다.

이러한 상황들을 방지하고자 Thread가 safe하게 프로그래밍하는 방법을 선택한다.

Thread를 safe하게 만드는 방법은 4가지가 있다.

  • Mutual Exclusion (상호 배제)
    - 간단하게 어느 하나의 쓰레드가 자원을 사용할때는, 다른 쓰레드는 해당 자원을 못 쓰게 하는 방법.
  • Atomic Operation (원자 연산)
    - 공유 자원의 연산을 원자적으로 분리, 실제로 데이터가 변경되는 순간에는 Lock을 걸고, 데이터가 변경되는 시간 동안은 다른 접근은 못하도록 하는 방법. (어플리케이션 내부, DB는 아님.)
  • Thread-Local Storage (쓰레드 지역 저장소)
    - 정확하게는 잘 이해 못 했지만, 공유되는 자원을 최소화 하라는 의미로 보인다. 예를 들면, 전역 변수를 많이 만들지 않는 것!
  • Re-Entrancy (재진입성)
    - 이 내용도 의미 자체는 이해가 되지만, 조금 더 깊이 있는 이해가 필요할 것 같다.
    - 상호배제와는 다르게 동시성을 가진다. 하지만 그렇더라도 실행 결과에는 영향이 없게 해야 한다는 내용.

3. 사용 예시

먼저 간단한 원리를 생각해보면 특정 쓰레드가 메소드나 변수에 접근 시, 해당 메소드나 변수에 Lock을 걸어줌으로써 처리가 가능하다.
다시 말하자면, 어떤 쓰레드가 메소드나 변수를 사용중이라면, 다른 쓰레드에서는 해당 메소드나 변수를 접근할 수 없도록 잠궈버리는 것이다.

class Count {
    private int count;
    public synchronized int view() {return count++;} // 메소드 Lock
}
---------------------------------------------------------------------
class Count {
    private Integer count = 0;
    public int view() {
        synchronized (this.count) { // 변수 Lock
            return count++;
        }
    }
}

메소드나 변수 앞에 synchronized를 사용하면, 암시적 락이 걸린다.

이렇게 하면 단 하나의 쓰레드만 접근이나 참조가 가능하다.
단, 변수의 경우는 객체에만 가능하며, int나 long같은 기본형 타입에는 Lock을 걸 수 없다.