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
'책정리 > EfectiveJava' 카테고리의 다른 글
지연 초기화는 신중히 사용하라 (0) | 2019.04.22 |
---|---|
wait와 notify보다는 동시성 유틸리티를 이용하라 (0) | 2019.04.21 |
스레드보다는 실행자, 태스크, 스트림을 애용하라 (0) | 2019.04.21 |
과도한 동기화는 피하라 (0) | 2019.04.17 |
공유 중인 가변 데이터는 동기화해 사용하라 (0) | 2019.04.03 |