synchronized 키워드: 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장
동기화
가변 데이터는 단일 스레드에서만 써야 한다
응답 불가와 안전 실패를 피하려면 동기화 메서드나 동기화 블록 안에서는 제어를 절대로 클라이언트에 양도하면 안 된다
동기화 영역에서는 가능 한 일을 적게 하는 것이 좋다.
동기화는 멀티코어 세상인 지금 성능을 급격히 저하 시키고, 합당한 이유가 있을 때만 내부에서 동기화하고, 동기화 여부는 문서화 하는 것이 좋다
실행자는 사용하기 매우 간단하다
// 생성
ExecutorServcice exec = Executors.newSingleThreadExecutor();
//실행
exec.execute(runnable);
//우아한 종료
exec.shutdown();
큐를 둘 이상의 스레드가 처리하게 하고 싶다면 스레드 풀을 생성해 처리하는 것이 좋다
작은 프로그램이나 가벼운 서버라면 Executors.newCachedThreadPool을 사용하는 것이 좋다
무거운 서버라면 스래드 개수를 고정한 Executors.newFixedThreadpool 혹은 직접 통제할 수 있는 ThreadPoolExecutor을 직접 사용하는 것이 좋다
wait와 notify는 올바르게 사용하기 어려우니 고수준 동시성 유틸리티를 사용하는 것이 좋다
동시성 컬랙션에서 동시성을 무력화하는 건 불가능하며, 외부에서 락을 추가로 사용하면 오히려 속도가 느려진다
Collections.sychronizedMap보다는 ConcurrentHashMap이 성능이 더 좋기 떄문에 ConcurrentHashMap을 사용하는게 좋다
시간 간격을 잴 때는 항상 System.currentTimeMillis가 아닌 System.nanoTime이 더 정확하니 nanoTime을 사용해야 한다
Wait 메서드를 사용할 때는 반드시 대기 반복문 관용구를 사용하고 반복문 밖에서는 절대로 호출하면 안된다
스레드를 전부 깨우는 notifyAll이 읿반적으로 하나의 스레드를 깨우는 notify보다 좋다
메서드 선언에 synchronized 한정자를 선언할지는 구현 이슈일 뿐 API에 속하지는 않아서 이것만으로 그 메서드가 스레드 안전하다고 믿기 어렵다
멀티스레드 환경에서도 API를 안전하게 사용하게 하려면 클래스가 지원하는 스레드 안전성 수준을 정확히 명시해야 한다.
스레드 안전성이 높은 순위
무조건적 스레드 안전 클래스를 작성할 때는 비공개 락 객체를 사용해지 하위 클래스에서 동기화 매커니즘을 깨뜨리는 걸 예방할 수 있다
지연 초기화: 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
//일반 초기화
private final FieldType field = computedFieldValue();
//지연 초기화
private synchronized FieldType getField(){
if(field == null)
field = computeFieldValue();
return field;
}
클래스 혹은 인스턴스 생성 시 초기화 비용은 줄지만 지연 초기화하는 필드에 접근하는 비용은 커진다
자주 호출하면 성능이 오히려 안좋아 질 수 있음
인스턴스 사용 비율이 낮고, 초기화 비용이 작으면 지연 초기화가 좋지만 일반적인 상황에서는 일반 초기화가 좋다
성능 때문에 정적 필드를 지연 초기화해야 한다면 지연 초기화 홀더 클래스 관용구를 사용하고 이중검사 관용구를 사용해야 한다.
정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식하기 어렵다
스레드는 당장 처리해야 할 작업이 없다면 실행돼서는 안 된다
Thread.yield와 스레드 우선순위에 의존해서는 안된다
스레드 우선순위는 이미 잘 동작하는 프로그램의 서비스 품질을 높이기 위해 드물게 쓰고, 프로그램을 고치는 용도로는 써서 안된다