본문 바로가기

책정리/EfectiveJava

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

API문서에 synchronized키워드가 있는 메서드는 스레드 안전하다고 할 수 있다.

하지만 이 말은 몇가지 틀린 이야기 일 수 도 있다.

멀티 스레드 환경에서도 API를 안전하게 사용하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야함

 

다음은 스레드 안전성 수준을 높은 순서대로 나열

 

불변(imuutable)

 - 상수와 같아서 외부 동기화도 필요 없음

 - String, Long, BigInteger

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

 - 수정될 수 있으나 내부에서 충실히 동기화하여 별도의 외부 동기화 없이 동시에 사용해도 안전

 - AtomicLong, ConcurrrentHashMap

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

 - 무조건적인 스레드 안전과 같으나, 일부 메서드는 동시에 사용하려면 외부 동기화가 필요

 - Collections.synchronized 래퍼 메서드가 반환한 컬렉션

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

 - 이 클래스의 인스턴스는 수정 될 수 있다.

 - 동시에 사용하려면 각각의 메서드 호출을 클라이언트가 선택한 외부 동기화 로직으로 감싸야 함

 - ArrayLIst, HashMap

스레드 적대적(thread-hostile)

 - 외부 동기화로 감싸더라도 멀티스레드 환경에서 안전하지 않다

-  동시성을 고려하지 않고 작성하다 보면 우연히 만들어 질 수 있다.

 

 

동기화에 대한 문서화

 

조건부 스레드 안전한 클래스는 주의하여 문서화 해야 함

어떤 순서로 호출할 때 외부 동기화 로직이 필요한지 순서대로 호출 하려면 어떤 락을 얻어야만 하는지 알려 줘야함.

 

환 타입만으로는 명확히 알수 없는 정적 팩터리라면 자신이 반환하는 객체의 스레드 안전성을 반드시 문서화 해야함.

Collections.synchronizedMap이 좋은 예다

 

 

외부에 공개된 LOck

외부에 공개된 락을 사용하면 유연한 코드를 만들 수 있지만 대가가 따른다

클라이언트가 공개된 락을 가지고 놓지 않는 서비스 거부 공격(denial-of-service attack)을 수행 할 수도 있다.

synchronized도 공개된 락이다.

그렇기 때문에 비공개 락을 사용해야 한다.

 

// 비공개 락 객체, final 선언!
private final Object lock = new Object();

public void someMethod() {
    synchronized(lock) {
        // do something
    }
}

 

finla로 선언한 이유는 우연히라도 락 객체가 교체되는 일을 예방한다. 

락 필드는 항상 final로 선언 하라

 

 

https://madplay.github.io/post/document-thread-safety

 

MadPlay's MadLife.

이펙티브 자바 82: 스레드 안전성 수준을 문서화하라

madplay.github.io