기본 패턴
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 | 기본적인 공유 상태 보호 |
ReentrantLock | lock 획득/해제 제어가 더 필요할 때 |
Atomic* | 단순 원자 연산 |
ConcurrentHashMap | 동시 접근 map |
| 불변 객체 | 공유 상태 자체를 줄일 때 |
주의할 점
thread safety는 "한 군데에 synchronized를 붙였다"로 끝나는 문제가 아닙니다. 어떤 상태가 공유되는지, 읽기와 쓰기가 어떤 순서로 일어나는지까지 함께 봐야 합니다.
참고 링크
3 sources