동시성
아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라
- 스레드가 가변 데이터를 공유한다면 그 데이터를 읽고 쓰는 동작은 반드시 동기화 해야함
- 동기화 하지 않으면 한 스레드가 수행한 변경을 다른 스레드가 보지 못할 수 있음
- 공유되는 가변 데이터를 동기화 하는데 실패하면 응답 불가 상태에 빠지거나 안전 실패로 이어질수 있음
아이템 79. 과도한 동기화는 피하라
- 과도한 동기화는 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 예측할 수 없는 동작을 낳기도 함
- 응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에게 양도하면 안됨
- 동기화 영역은 가능한 할 일을 적게 하는것
- 동기화 여부를 정확하게 문서에 남겨야함
아이템 80. 스레드보다는 실행자, 태스크, 스트림을 애용하라
- java.util.concurrent 패키지는 실행자 프레임워크라고 하는 인터페이스 기반의 유연한 태스크 실행 기능을 제공
- 다음과 같은 작업 큐를 구현가능
// 실행자 생성
ExecutorService exec = Executors.newSingleThreadExecutor();
// 실행자에게 태스크를 넘김
exec.execute(runnable);
// 실행자를 우아하게 종료시키는 방법
exec.shutdown();
- 이 이외도 많은 기능을 제공
- 특정 테스크가 완료되는것을 기다림
- 태스크중 아무거나 하나가 완료되는것을 기다림
- 완료된 태스크를 차례로 받음
- 태스크를 특정 시간에 또는 주기적 실행하게 함
아이템 81. wait와 notify보다는 동시성 유틸리티를 애용하라
- wait, notify는 올바르게 사용하기가 아주 까다로우니 고수준 동시성 유틸리티(java.util.concurrent)를 사용
- 동시성 컬렉션에서 동시성을 무력화하는건 불가능 하며, 외부에서 락을 걸면 속도가 느려짐
아이템 82. 스레드 안전성 수준을 문서화하라
- 멀티스레드 환경에서도 API를 안전하게 사용하게 하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야함
- 다음은 스레드 안전성이 높은 순으로 나열
- 불변
- 무조건 스레드에 안전
- 조건부 스레드에 안전
- 스레드에 안전하지 않음
- 스레드 절대적
아이템 83. 지연 초기화는 신중히 사용하라
- 지연 초기화(lazy initialization)은 필드의 초기화 시점을 사용시점으로 늦추는 기법
- 값이 사용되지 않으면 전혀 초기화가 일어나지 않음
- 지연 초기화는 주로 최적화 용도로 사용되지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있음
- 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 나음
- 성능 때문에 정적 필드 지연 초기화해야 한다면 지연 초기화 홀더 클래스(lazy initialization holder class) 관용구를 사용
private static class FieldHolder {
static final FieldType field = computerFieldValue();
}
// getField 호출시 field가 초기화됨
private static FieldType getField() { return FieldHolder.field; }
아이템 84. 프로그램의 동작을 스레드 스케줄러에 기대지 말라
- 스레드에 대한 스케줄링은 운영체제의 스레드 스케줄러가 결정
- 정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식이 어려움
- Thread.yield나 스레드 우선순위에 의존해서는 안됨