-
item 79 과도한 동기화는 피하라책/이펙티브 자바 2022. 4. 23. 11:58
ITEM 79 과도한 동기화는 피하라
과도한 동기화는 성능을 떨어뜨리고 교착상태에 빠뜨리고 예측할 수 없는 동작을 낳기도 한다
- 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안 된다
- 동기화된 영역 안에서는 재정의할 수 있는 메서드는 호출하면 안된다
- 클라이언트가 넘겨준 함수 객체를 호출해서도 안된다
- 동기화된 영역을 포함한 클래스 관점에서는 이런 메서드는 다른 차원에서 온 외계인이다
- 그 메서드가 무슨 일을 할지 알지 못하며 통제할 수도 없다는 뜻이다
- 외계인 메서드(alien method)가 하는 일에 따라 동기화된 영역은 예외를 일으키거나 교착상태에 빠지거나 데이터를 훼손할 수도 있다
열린 호출(open call)
- 동기화 영역 바깥에서 호출되는 외계인 메서드를 말한다
- 외계인 메서드는 얼마나 오래 실행될지 알 수 없는데 동기화 영역 안에서 호출 된다면 그동안 다른 스레드는 보호된 자원을 사용하지 못하고 대기 해야만 한다
- 따라서 열린 호출은 실패방지 효과 외에도 동시성 효율을 크게 개선해준다
기본 규칙은 동기화 영역에서는 가능한 한 일을 적게 하는 것이다
- 락을 얻고 공유 데이터를 검사하고 필요하면 수정하고 락을 놓는다
- 오래 걸리는 작업이라면 item 78의 지침을 어기지 않으면서 동기화 영역 바깥으로 옮기는 방법을 찾아보자
성능에 관한 측면
- 자바 동기화 비용은 빠르게 낮아져 왔지만 과도한 동기화를 피하는 일은 어느때보다 중요하다
- 멀티코어가 일반화된 시기인 만큼 과도한 동기화가 초래하는 진짜 비용은 락을 얻는 데 드는 CPU 시간이 아니다
- 경쟁하느라 낭비하는 시간, 병렬로 실행할 기회를 잃고 모든 코어가 메모리를 일관되게 보기 위한 지연시간이 진짜 비용이다
- 가상머신의 코드 최적화를 제한한다는 점도 과도한 동기화의 또 다른 숨은 비용이다
가변 클래스를 작성하려거든 다음 두 선택지 중 하나를 따르자
- 동기화를 전혀 하지 말고 그 클래스를 동시에 사용해야 하는 클래스가 외부에서 알아서 동기화하게 하자
- java.util은 Vector와 Hashtable을 제외하고 첫번째 방식을 택했다
- 동기화를 내부에서 수행해 스레드 안전한 클래스로 만들자
- 클라이언트가 외부에서 객체 전체에 락을 거는 것보다 동시성을 월등히 개선할 수 있을 때만 두 번째 방법을 선택해야 한다
- java.util.concurrent는 두번째 방식을 택했다
- 자바도 초창기에는 이 지침을 따르지 않은 클래스가 많았다
- StringBuffer 인스턴스는 거의 항상 단일 스레드에서 쓰였지만 내부적으로 동기화를 수행해서 뒤늦게 StringBuilder가 등장했다
- StringBuilder는 그저 동기화 하지 않은 StringBuffer다
- 비슷한 이유로 스레드 안전한 의사 난수 발생기인 java.util.Random은 동기화하지 않은 버전인 java.util.concurrent.ThreadLocalRandom으로 대체되었다
- 선택하기 어렵다면 동기화하지 말고 문서에 "스레드 안전하지 않다"고 명시하자
클래스를 내부에서 동기화하기로 했다면?
- 락분할(lock splitting), 락 스트라이핑(lock striping), 비차단 동시성 제어(nonblocking concurrency control) 등 다양한 기법을 동원해 동시성을 높여줄 수 있다
여러 스레드가 호출할 가능성이 있는 메서드가 정적 필드를 수정한다면 그 필드를 사용하기 전에 반드시 동기화해야 한다
- 비결정적 행동도 용인하는 클래스라면 상관없다
교착상태와 데이터 훼손을 피하려면 동기화 영역 안에서 외계인 메서드를 절대 호출하지 말자
일반화해 이야기하면 동기화 영역 안에서의 작업은 최소한으로 줄이자
가변 클래스를 설계할 때는 스스로 동기화해야 할지 고민하자
멀티코어 세상인 지금은 과도한 동기화를 피하는게 과거 어느 때보다 중요하다
합당한 이유가 있을 때만 내부에서 동기화 하고 동기화 여부를 문서에 명확히 밝히자
'책 > 이펙티브 자바' 카테고리의 다른 글
item 81 wait와 notify보다는 동시성 유틸리티를 애용하라 (0) 2022.04.25 item 80 스레드보다는 실행자, 태스크, 스트림을 애용하라 (0) 2022.04.23 item 78 공유 중인 가변 데이터는 동기화해 사용하라 (0) 2022.04.23 item 77 예외를 무시하지 말라 (0) 2022.04.23 item 76 가능한 한 실패 원자적으로 만들라 (0) 2022.04.23