728x90
새로운 사실!
wait 그리고 notify는 올바르게 사용하기가 아주 까다롭다. 따라서 고수준 동시성 유틸리티를 사용하는 것이 좋다.
wait와 notify란?
: 스레드의 상태 제어를 위한 메소드
wait : 가지고 있던 고유 락을 해제하고, 스레드를 잠들게 하는 역할을 하는 메서드
notify : 잠들어 있던 스레드 중 임의로 하나를 골라 깨우는 역할을 하는 메서드
java.util.concurrent의 고수준 유틸리티
- 실행자 프레임워크
- 동시성 컬렉션
- List, Queue, Map과 같은 표준 컬렉션 인터페이스에 동시성을 가미해 구현한 고성능 컬렉션
- 높은 동시성에 도달하기 위해 동기화를 각자 내부에서 수행
- 따라서 동시성 컬렉션에서 동시성을 무력화하는 것은 불가능하다. 외부에서 락을 추가로 사용하면 오히려 속도가 느려진다.
- 이 때문에 여러 기본 동작을 하나의 원자적 동작으로 묶는 '상태 의존적 수정'메서드가 추가됨.
- 동기화한 컬렉션을 낡은 유산으로 만들어버린다.
- 동기화한 컬렉션인 Collections.synchronizedMap 보다는, 동시성 컬렉션인 ConcurrentHashMap을 사용하는게 훨씬 좋다.
- 극적인 성능 개선!
- 동기화 장치
- 스레드가 다른 스레드를 기다릴 수 있게 하여, 서로 작업을 조율할 수 있게 해준다.
- Phaser > CountDownLatch, Semaphore > CyclicBarrier, Exchanger 순으로 자주 쓰인다.
- CountDownLatch
- 일회성 장벽
- 하나 이상의 스레드가 또 다른 하나 이상의 스레드 작업이 끝날 때까지 기다리게 한다.
- int값을 받는데, 이 값을 통해 countDown을 몇 번 호출해야 대기중인 스레드를 깨울지 결정한다.
새로운 코드를 작성할 때는 wait, notify보다는 동시성 유틸리티를 사용하라는 것이 item 81의 내용이지만,
어쩔 수 없이 wait과 notify를 사용해야 하는 경우가 있다.
wait 메소드의 경우,
- 스레드가 어떤 조건이 충족되기를 기다리게 할 때 사용한다.
- 락 객체의 wait 메서드는 반드시 그 객체를 잠근 동기화 영역 안에서 호출해야 한다.
- 반드시 대기 반복문(wait loop) 관용구를 사용해야 한다.
- 반복문 밖에서는 절대로 호출하면 안된다.
대기한 이후에 조건을 검사하여 조건을 충족하지 않았을 때 다시 대기하게 하는 경우도 있다.
이는 조건이 만족되지 않아도 스레드가 깨어날 수 있는 상황이 몇 가지 있기 때문이다. (안전 실패 예방)
- notify를 호출하여 대기 중인 스레드가 깨어나는 사이에 다른 스레드가 락을 거는 경우
- 조건이 만족되지 않았지만 실수 혹은 악의적으로 notify를 호출하는 경우
- 대기 중인 스레드 중 일부만 조건을 충족해도 notifyAll로 모든 스레드를 깨우는 경우
- 대기 중인 스레드가 드물게 notify 없이 깨어나는 경우 (허위 각성)
notify 메소드의 경우,
- 일반적으로 notify보다 notifyAll을 사용하는 것이 합리적이고 안전하다.
- 모든 스레드가 같은 조건을 기다리고, 조건이 한 번 충족될 때마다 단 하나의 스레드만 혜택을 받을 수 있다면 notify를 사용하는 것이 최적화를 하는 방법이다.
- 그러나... 위 조건을 만족하더라도 notifyAll을 사용하는 것이 나을 수 있다.
- 관련 없는 스레드가 실수로 or 악의적으로 wait를 호출하는 공격으로부터 보호할 수 있기 때문이다.
핵심 정리
wait와 notify를 직접 사용하는 것은 어셈블리어로 프로그래밍 하는 것과 같다.
java.util.concurrent는 고수준 언어에 비유할 수 있다.
새롭게 코드를 작성할 때는 wait과 notify를 사용하지 말아라.
만약 레거시 코드에서 이를 어쩔 수 없이 사용해야 한다면, wait는 항상 while문 안에서 호출해라.
또한, 일반적으로 notify보다는 notifyAll을 사용해라.
혹시라도 notify를 사용한다면, 응답 불가 상태에 빠지지 않도록 각별히 주의하자.
728x90
'JAVA > Effective Java' 카테고리의 다른 글
item 87. 커스텀 직렬화 형태를 고려해보라 (0) | 2022.09.13 |
---|---|
item 82. 스레드 안정성 수준을 문서화하라 (0) | 2022.09.01 |
item 80. 스레드보다는 실행자, 테스크, 스트림을 애용하라 (0) | 2022.08.31 |
item 79. 과도한 동기화는 피하라 (0) | 2022.08.31 |
공지 (0) | 2022.07.30 |
댓글