본문 바로가기

STUDY/이펙티브자바

아이템 82: 스레드 안정성 수준을 문서화하라

스레드 안정성 문서화

 

클래스를 개발할 때, 해당 클래스를 사용할 클라이언트들을 위하여 필요한 정보들을 주석으로 작성하여 문서화 해야한다. 아무런 설명이 없다면 클라이언트는 추측과 가정을 통해 그 클래스를 사용하게 된다. 특히 스레드 안정성에 대한 정보는 설명이 필요한 중요한 정보 중 하나이다. 스레드 안정성이 잘못되면 프로그램에 심각한 오류가 발생할 것이다.

 

 


스레드 안정성 수준

 

  • 불변(immutable): 이 클래스의 인스턴스는 마치 상수와 같아서 외부 동기화가 필요없다.
  • 무조건적 스레드 안전(unconditionally thread-safe): 이 클래스의 인스턴스는 수정될 수 있으나, 내부에서 충실히 동기화하여 별도의 외부 동기화 없이 동시에 사용해도 안전하다.
  • 조건부 스레드 안전(conditionally thread-safe): 무조건적 스레드 안전과 같으나, 일부 메서드는 동시에 사용하려면 외부 동기화가 필요하다.
  • 스레드 안전하지 않음(not thread-safe): 이 클래스의 인스턴스는 수정될 수 있다. 동시에 사용하려면 각각의 메서드 호출을 클라이언트가 선택한 외부 동기화 메커니즘으로 감싸야한다.
  • 스레드 적대적(thread-hostil): 이 클래스는 모든 메서드 호출을 외부 동기화로 감싸더라도 멀티스레드 환경에서 안전하지 않다.

 

 


SynchronizedMap - 조건부 스레드 안전

 

// 런타임 에러 발생
Map<Integer, Integer> syncMap = Collections.synchronizedMap(new HashMap<Integer, Integer>());
Set<Integer> keys = syncMap.keySet();
for(int i = 0; i < 1000; i++) {
    syncMap.put(i, i);
}

Thread tr1 = new Thread(() -> {
    for(int i = 0; i < 1000; i++) {
        syncMap.remove(i);
    }			
});
Thread tr2 = new Thread(() -> {
    for(Integer key : keys) {
        System.out.println(syncMap.get(key));
    }			
});

tr1.start();
tr2.start();

SynchronizedMap은 대표적인 조건부 스레드 안전 수준을 가진 객체이다. Collections.synchronizedMap 메소드는 Map 객체를 전달하면 동기화 처리가 된 SynchronizedMap 객체를 반환한다. 여러 스레드에서 해당 객체에 접근하여도 대부분의 경우 문제 없이 동작한다. 하지만 SynchronizedMap 객체의 keySet를 사용할 때 문제가 발생한다. 다행히 해당 내용은 문서화된 주석에 설명되어 있어서, 사용자는 해당 내용을 보고 대응할 수 있다.

 

Returns a synchronized (thread-safe) map backed by the specifiedmap. In order to guarantee serial access, it is critical that all access to the backing map is accomplishedthrough the returned map.

It is imperative that the user manually synchronize on the returnedmap when traversing any of its collection views via Iterator, Spliterator or Stream: 
Map m = Collections.synchronizedMap(new HashMap());
  ...
Set s = m.keySet();  // Needn't be in synchronized block
  ...
synchronized (m) {  // Synchronizing on m, not s!
  Iterator i = s.iterator(); // Must be in synchronized block
  while (i.hasNext())
      foo(i.next());
}

 

 

728x90