티스토리 뷰

728x90
반응형

스레드 안전성 수준을 문서화하라


  • 한 메서드를 여러 스레드가 동시에 호출하는 경우 그 메서드가 어떻게 동작하냐는 해당 클래스와 이를 사용하는 클라이언트 사이의 계약과 같습니다. 문서화를 제대로 해놓지 않는다면 클라이언트는 추측을 하여 사용할테고 이 추측이 틀린경우 문제가 발생할 수 있습니다. 그렇기 때문에 스레드 안전성을 문서화할 필요성이 있습니다.

 

💡 스레드 안전성 수준

  • 멀티 스레드 환경에서는 API를 안전하게 사용하기 위해서는 클래스가 지원하는 스레드 안전성 수준을 명확히 명시해야 합니다.

📜 불변(Immutable)

  • 이 클래스의 인스턴스는 마치 상수와 같아서 외부 동기화가 필요 없습니다.
  • Ex) String, Long, BigInteger

📜 무조건적 스레드 안전(unconditionally thread-safe)

  • 이 클래스의 인스턴스는 수정될 수 있지만 내부에서 동기화를 충분히 하였기 때문에 외부 동기화없이 사용해도 안전합니다.
  • Ex) AtomicLong, ConcurrentHashMap

📜 조건부 스레드 안전(conditionally thread-safe)

  • 일부 메서드를 동시에 사용하기 위해서는 외부 동기화가 필요합니다.
  • Ex) Collections.synchronized 래퍼 메서드가 반환하는 컬렉션들

📜 스레드 안전하지 않음(not thread-safe)

  • 이 클래스의 인스턴스는 수정될 수 있습니다.멀티 스레드 환경에서 사용하기 위해서는 메서드 호출자가 외부 동기화 매커니즘으로 감싸야 합니다.
  • Ex) ArrayList, HashMap

📜 스레드 적대적(thread-hostile)

  • 이 클래스는 모든 메서드 호출을 외부 동기화를 한다하더라도 멀티스레드 환경에서는 안전하지 않습니다.
  • 이 수준의 클래스는 일반적으로 정적 데이터를 아무 동기화 없이 수정합니다.

💡 서비스 거부 공격(denial-of-service attack)

  • 클래스가 외부에서 사용할 수 있는 락을 제공할 경우 클라이언트에서 일련의 메서드 호출을 원자적으로 수행할 수 있습니다. 이러한 방식은 클래스 내부에서 처리하는 고성능 동시성 제어 매커니즘과 혼용할 수 없게 만듭니다.
  • 클라이언트가 공개된 락을 돌려주지 않는 서비스 거부 공격을 수행할 수 있습니다. 그렇기 때문에 이러한 공격을 막기 위해서는 synchronized 메서드 대신 비공개 락 객체를 사용해야 합니다.
  • lock 필드는 외부에서 볼 수 없고 final 키워드로 선언하였기 때문에 락 객체를 교체할 수 있는 일도 사전에 예방합니다. 이런 비공개 락 객체 관용구는 무조건적 스레드 안전 클래스에서만 사용할 수 있는데, 조건부 스레드 안전 클래스같은 경우 특정 호출 순서에 필요한 락이 무엇인지 클라이언트에게 알려줘야 하기 때문에 이 관용구를 사용할 수 없습니다.
// 비공객 락 객체, final로 선언하여 락 교체를 사전에 방지합니다.
private final Object lock = new Object();

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

 

 

 

 

 

 

 

728x90
반응형