Item 70. 스레드 안정성에 대해 문서로 남겨라

스레드 안정성에 대한 수준별 요약

  1. 변경 불가능(immutable) : 이 클래스로 만든 객체들은 상수다. 따라서 외부적인 동기화 메커니즘 없이도 병렬적 이용이 가능하다. String, Long, BigInteger등이 있다.
  2. 무조건적 스레드 안전성(unconditionally thread-safe) : 이 클래스의 객체들은 변경이 가능하지만 적절한 내부 동기화 메커니즘을 갖추고 있어 외부적으로 동기화 메커니즘을 적용하지 않아도 병렬적으로 사용 할수 있다. Random, ConcurrentHashMap등이 있다.
  3. 조건부 스레드 안정성(conditionally thread-safe) : 외부적 동기화 없이는 병렬적으로 사용할수 없는 겍체, 예를 들어, Collections.synchronized계열 함수가 반환하는 포장 객체를 사용할때, 이런 객체들의 반복자(iterator)는 외부적 동기화 없이는 병렬적으로 사용할수 없다.
  4. 스레드 안정성 없음 : 해당 객체들을 병렬적으로 사용하려면, 함수를 호출하는 부분을 클라이언트 코드에서 선택한 외부적 동기화 수단으로 감싸야 한다. ArrayListHashMap같은 일반 용도의 컬렉션 구현체들 이에 해당한다.
  5. 다중 스레드에 적대적(thread-hostile) : 이런 클래스의 객체는 설사 함수를호출하는 모든 부분을 외부적 동기화 수단으로 감싸더라도 안전하지 않다. 이런 클래스가 되는 것은 보통, 동기화 없이 정적 데이터(static data)를 변경하기 때문이다.

조건부 스레드 안정성 반복처리를 위한 숙어

Collections.synchronizedMap의 문서를 보면 아래와 같이 명세되어 있다.

Map<K, V> m = Collections.synchronizedMap(new HashMap<K, V>());
...
Set<K> s = m.ketSet();  // 동기화 블록 안에 있을 필요가 없음
...
synchronized(m) {   // s가 아니라 m에 동기화
    for (K key : s)
        key.f();
}

이 지침을 따르지 않을 경우 프로그램은 비결정적(non-deterministic)으로 동작할 수 있다.

private 락 객체 패턴 숙어

// DoS 공격을 피하기 위한 private lock 객체 숙어
private final Object lock = new Object(); // 이렇게 하면 실수로, lock필드의 내용을 변경하는 것을 막을 수 있음

public void foo() {
    synchronized(lock) {
        ...
    }
}

private락 객체 패턴은 무조건적 스레드 안전성을 제공하는 클래스에만 적용할 수 있다.

결론