공유하는 가변 데이터에 접근 시 동기화 하자
쓰는 메소드 뿐만 아니라 읽는 메소드도 동기화 처리를 해야 한다.
32비트 변수에 대한 할당 연산은 원자적으로 처리되기 때문에 동기화가 필요하지 않다.
하지만 스레드 간에 공유되는 데이터라면 메모리 가시성을 위해서 동기화 처리가 필요하다.
메모리 가시성만 확보하면 되기 때문에 volatile 키워드를 사용해도 된다.
int 변수에 대한 ++ 연산은 단일 연산처럼 보이지만 실제로는 3단계로 나뉘어져 있기 때문에 동기화 처리가 필요하다.
지나친 동기화는 피하자
동기화 블록 안에서 외계인 메소드 호출을 하지 말아야 한다.
wait와 notify 대신 동시성 유틸리티를 사용하자
wait, notify를 사용할 이유가 거의 없다. 직접 사용하는 것은 "동시성 어셈블리 언어"로 프로그래밍 하는 것과 같다.
올바르게 사용하기 어렵다면 고수준 동시성 유틸리티를 사용해야 한다.
(ConcurrentHashMap, BlockingQueue, CountDownLatch, Semaphore, CyclicBarrier, Exchanger 등)
새로 작성하는 코드에는 가급적 사용하지 말고 유지보수 한다면 다음을 지키자.
- 표준 이디엄을 사용해서 while 루프에서 wait 메소드를 호출
- 일반적으로 notify보다는 notifyAll을 사용해야 함
- notify를 사용한다면 스레드 활동성 보장에 신경써야함
스레드 안전을 문서화 하자
모든 클래스는 자신의 스레드 안전 속성을 명확하게 문서화해야 함.
내용을 작성할 때는 신중하게 문장을 기술하거나 스레드 안전 주석을 사용한다.
synchronized는 javadoc에 나오지 않기 때문에 알 수 없다.
스레드 안전 수준 5가지(일반적인 경우)
- 불변
- 무조건적 스레드 안전
- 조건적 스레드 안전
- 스레드 안전하지 않음
- 스레드 적대
조건적 스레드 안전 클래스는 외부 동기화가 필요할 수 있으므로 락 획득 필요성과 순서에 대해 문서화해야함
무조건적 스레드 안전 클래스는 동기화된 메소드 대신 private 락 객체를 사용하면 클라이언트나 서브 클래스에서 동기화를 방해하는 것을 막을 수 있다.
늦 초기화를 분별력 있게 사용하자
늦 초기화는 본래 최적화 관점에서 필요하지만 인스턴스가 완벽하게 생성되지 않은 상태에서 사용되는 것을 방지하는 목적으로 사용될 수도 있음
대부분의 최적화에서 그렇듯 "필요하지 않으면 하지 말자".
다중 스레드에서 늦 초기화는 쉽지 않고 대부분의 상황에서는 정상적인 초기화가 늦 초기화보다 좋다.
만약 사용해야 한다면 다음과 같은 방법을 사용하자.
- 인스턴트 필드에 적용할 때는 synchronized 키워드로 동기화 처리
- static 필드에 적용한다면 늦 초기화 홀더 클래스 이디엄을 사용
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field; }
// 근래의 VM은 초기화하는 필드의 접근만 동기화 시킴.
// 일단 클래스가 초기화되면 이후에 그 필드 접근 시는 동기화 코드가 포함되지 않도록 VM이 코드를 조정함
- 인스턴트 필드의 성능을 고려한다면 더블 체크락을 사용(자바 병렬 프로그래밍 책에서 더블 체크락은 문제가 있다고 나와있었는데)
스레드 스케줄러에 의존하지 말자
스레드 그룹을 사용하지 말자
'개발 > 병렬 프로그래밍' 카테고리의 다른 글
자바 병렬 프로그래밍 - 16장 메모리 모델 (0) | 2016.09.09 |
---|---|
자바 병렬 프로그래밍 - 15장 단일 연산 변수와 넌블로킹 동기화 (0) | 2016.09.08 |
자바 병렬 프로그래밍 - 13장 명시적인 락 (0) | 2016.09.07 |
자바 병렬 프로그래밍 - 11장 성능, 확장성 (0) | 2016.09.04 |
자바 병렬 프로그래밍 - 10장 활동성 최대로 높이기 (0) | 2016.09.01 |