1. 스레드 안전성 수준
한 메서드를 여러 쓰레드가 동시에 호출했을 때, 그 메서드가 어떻게 동작하는지는 해당 클래스를 사용하는 클라이언트 입장에선 굉장히 중요하다. API 문서에 아무런 언급이 없으면 그 클래스 사용자는 혼자서 나름의 가정을 해야하기 때문이다.
멀티쓰레드 환경에서 API 를 안전하게 사용하기 위해선, 클래스가 지원하는 쓰레드 안전성 수준을 정확히 명시하자.
아래는 쓰레드 안전성이 높은 순서이다. 문서화할 때 참고하도록 하자.
1. 불변 : 이 클래스의 인스턴스는 상수와 같아서, 외부 동기화가 필요 없다. (ex. String, Long, BigInteger)
2. 무조건적 쓰레드 안전 : 이 클래스의 인스턴스는 수정될 수 있으나, 내부 동기화를 통해 외부 동기화 없이 사용해도 안전하다.
(ex. AtomicLong, ConcurrentHashMap)
3. 조건부 쓰레드 안전 : 무조건적 쓰레드 안전과 같으나, 일부 메서드에 한해 외부 동기화가 필요하다.
(ex. Collections.synchronized 래퍼 메서드)
4. 쓰레드 안전하지 않음 : 이 클래스의 인스턴스는 수정될 수 있어 클라이언트가 외부 동기화 매커니즘으로 감싸야 한다.
(ex. ArrayList, HashMap)
5. 쓰레드 적대적 : 이 클래스의 모든 메서드 호출을 외부 동기화로 감싸더라도, 멀티쓰레드 환경에서 안전하지 않다.
이 중, 조건부 쓰레드 안전한 클래스는 주의해서 문서화하자. 어떤 순서로 호출할 때 외부 동기화가 필요한지, 그리고 그 순서로 호출하려면 어떤 락을 얻어야 하는지 알려줘야 한다. 예를 들어, Collections.synchronizedMap 의 API 문서에는 아래와 같이 작성되어 있다.

/* synchronizedMap이 반환한 맵의 컬렉션 뷰를 순회하려면 반드시 그 맵을 락으로 사용해 수동으로 동기화하라. */
Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // 동기화 블록 밖에 있어도 된다.
...
synchronized (m) { // s가 아닌 m을 사용할때 동기화해야 한다!
Iterator i = s.iterator(); // 등기화 블록 안에 있어야한다.
while (i.hasNext())
foo(i.next());
}
/* 이대로 따르지 않으면 동작을 예측할 수 없다. */
보통은 쓰레드 안전성을 클래스의 문서화 주석에 기재하지만, 독특한 특성의 메서드라면 해당 메서드의 주석에 기재하자.
'1️⃣ 백앤드 > 이펙티브 자바' 카테고리의 다른 글
| [아이템84] 프로그램의 동작을 스레드 스케줄러에 기대지 말라 (0) | 2024.12.19 |
|---|---|
| [아이템83] 지연 초기화는 신중히 사용하라 (0) | 2024.12.19 |
| [아이템81] wait 와 notify 보다는 동시성 유틸리티를 애용해라 (0) | 2024.12.18 |
| [아이템80] 스레드보다는 실행자, 태스크, 스트림을 애용하라 (0) | 2024.12.16 |
| [아이템79] 과도한 동기화는 피하라 (0) | 2024.12.12 |