Java동시성과 스레드

synchronization, lock, thread safety

여러 thread가 같은 상태를 건드릴 때 왜 문제가 생기고, synchronized와 lock을 어떤 기준으로 고를지 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

java
public class Counter {
    private int value;

    public synchronized void increment() {
        value++;
    }

    public synchronized int getValue() {
        return value;
    }
}

설명

  • thread safety 문제는 "코드 한 줄"보다 "읽기-수정-쓰기" 같은 여러 단계가 엮일 때 생깁니다. value++조차 내부적으로는 원자적이지 않을 수 있기 때문에 여러 thread가 동시에 접근하면 결과가 꼬일 수 있습니다.
  • synchronized는 Java에서 가장 기본적인 mutual exclusion 도구입니다. 메서드나 블록 단위로 한 번에 한 thread만 들어오게 만들어 공유 상태를 보호합니다.
  • 하지만 모든 공유 상태를 무조건 lock으로 감싸는 것이 능사는 아닙니다. lock 범위가 넓으면 병렬성이 떨어지고, 여러 lock을 섞으면 deadlock 위험도 생깁니다.
  • 그래서 실무에서는 먼저 "정말 공유 상태가 필요한가", "불변 객체로 바꿀 수 없는가", "thread-safe 컬렉션이나 higher-level API로 해결할 수 없는가"를 먼저 봅니다. lock은 종종 마지막 수단에 가깝습니다.
  • ReentrantLock, AtomicInteger, ConcurrentHashMap 같은 도구는 synchronized보다 더 세밀한 제어를 줄 수 있지만, 그만큼 개념 부담도 커집니다. 입문 단계에서는 shared mutable state 자체를 줄이는 사고가 더 중요합니다.

빠른 정리

방법잘 맞는 상황
synchronized기본적인 공유 상태 보호
ReentrantLocklock 획득/해제 제어가 더 필요할 때
Atomic*단순 원자 연산
ConcurrentHashMap동시 접근 map
불변 객체공유 상태 자체를 줄일 때

주의할 점

thread safety는 "한 군데에 synchronized를 붙였다"로 끝나는 문제가 아닙니다. 어떤 상태가 공유되는지, 읽기와 쓰기가 어떤 순서로 일어나는지까지 함께 봐야 합니다.

참고 링크

3 sources