인프런 커뮤니티 질문&답변

식빵님의 프로필 이미지
식빵

작성한 질문수

자바 동시성 프로그래밍 [리액티브 프로그래밍 Part.1]

스레드 간 협력 - wait() & notify()

notifyAll 이 더 선호되는 이유에 대한 조금 더 detail 한 설명 요청드립니다.

해결된 질문

작성

·

928

1

안녕하세요 선생님, 질문이 있어서 글 올립니다.

[스레드 간 협력 - wait() & notify() 강의 - 6:23 ] 즈음에

"notifyAll 이 더 선호된다"... 라고 했는데,
제가 뭘 놓친 걸 수도 있지만, 잘 이해가 안됩니다.

 

왜 notfiyAll 이 선호되는지에 대한 핵심적인 이유를 알려주실 수 있을까요?

답변 3

2

정수원님의 프로필 이미지
정수원
지식공유자

대략 두가지 정도로 말씀 드릴수 있습니다.

첫 번째는 모니터는 하나의 조건변수만 가질 수 있고 그렇기 때문에 wait 하는 스레드는 제각각 여러가지 이유로 대기하는 것이지만 결론은 모든 스레드가 동일한 조건변수의 큐에 저장된다는 것을 의미합니다.

이런 경우에 notify() 할 경우 조건변수에 대기하고 있는 스레드 중 하나가 깨어날 경우 해당 스레드가 대기에서 빠져나갈 수 있는 조건이 부합하는지 아닌지 알 수가 없습니다. 만약 대기에서 깨어난 스레드가 여전히 조건에 부합하지 못한다면 다시 대기하게 됩니다.

그러나 notifyAll() 을 할 경우 모든 스레드가 깨어나기 때문에 조건에 부합하는 스레드는 대기에서 깨어나게 되고 어플리케이션이 정상적으로 제 기능을 발휘하게 됩니다.


두번째는 경쟁상태 관련 문제입니다.

notify() 는 하나의 스레드만 깨우기 때문에 모든 스레드는 대기에서 빠져나오기 위한 경쟁상태에 있게 됩니다.
즉 어떤 스레드가 깨어날 지 모르는 불확실한 상태에 있다고 볼 수 있습니다.

그러나 notifyAll() 는 모든 스레드가 공정하게 깨어나기 때문에 경쟁상태가 덜합니다.

물론 이 부분은 개념적인 부분입니다. 경쟁상태에서 하나의 임의의 스레드가 깨어나 실행하게 할 것인가 아니면 모든 스레드를 깨워서 공정하게 실행하게 할 것인가의 문제입니다.

그리고 이 문제는 서두에서 말씀드렸듯이 하나의 모니터 안에 있는 조건변수 큐에 여러 상황에 놓인 스레드들을 모두 대기하기 때문에 발생하는 문제입니다.

만약 조건 변수를 각 상황에 따라 여러개 생성하고 관리할 수 있다면 오히려 notify() 가 더 좋은 선택일 수 있습니다.

그리고, notifyAll() 은 모든 대기 중인 스레드를 깨우기 때문에 불필요한 컨텍스트 스위칭과 자원 경쟁을 증가시킬 수 있다는 단점도 있습니다.

이런 특징들을 잘 판단해서 사용해야 합니다.

식빵님의 프로필 이미지
식빵
질문자

굉장히 다양한 관점에서 바라봐야 되는군요 😅.
덕분에 좀 더 보는 시야가 넓어진 거 같습니다.

특히 락을 취득하기 위한 경쟁 뿐만 아니라,

대기에서 빠져나오기 위해서도 경쟁을 한다는 건 생각해본 적이 없던 거 같습니다.

친절한 설명 감사합니다. 오늘도 많이 배우고 갑니다!

1

식빵님의 프로필 이미지
식빵
질문자

선생님 답변과 더불어서 보면 좋은 StackOverflow 글이 있어서 공유합니다.

Java: notify() vs. notifyAll() all over again
https://stackoverflow.com/a/3186336

선생님이 답변 주신대로 하나의 WaitSet서로 다른 이유로 wait 상태에 놓인 쓰레드가 모두 담아질 때 문제가 생기는데요,

이러한 문제가 정확히 어떻게 발생할 수 있는지에 대한 자세한 예시를 이 글에서 보여줍니다.

ps. 해당 글을 읽을 때는 [Monitor - 모니터 - 1] 강의 27 분 즈음에 나오는 그림이 머리에 그려두고 읽으면 더 도움이 됩니다.

1

안녕하세요, 인프런 AI 인턴입니다.

식빵님, notifyAll()이 선호되는 이유에 대해 설명드리겠습니다. 대기하고 있는 스레드들 중에서 어떤 스레드가 깨어나야 올바른 동작을 수행할 수 있을지 결정하기는 어렵습니다. notify() 메소드는 하나의 스레드만을 깨우기 때문에, 올바른 스레드가 아닌 경우 원하는 동작을 이어가지 못할 수 있습니다. 반면, notifyAll() 메소드는 모든 대기 스레드를 깨우므로, 적어도 하나의 올바른 스레드가 깨어나 작업을 이어갈 수 있습니다.

그러나, notifyAll() 사용 시 불필요한 스레드들까지 깨어나기 때문에 성능상의 문제가 발생할 수도 있습니다. 따라서 어떤 메소드를 사용할지는 상황에 따라 적절히 선택해야 합니다. 좀 더 상세한 내용은 자바의 Concurrency 관련 자료를 참고하시면 좋을 것 같습니다.

이해에 도움을 드리기 위해 관련 문서나 토론을 찾아보실 수 있는데, Spring Boot에서도 다양한 프로그래밍 문제에 대한 ‘How-to’ 가이드 섹션을 제공하고 있으니 참고하시면 좋겠습니다. 질문이나 자세한 내용 추가는 스택오버플로우(Stack Overflow)에서 spring-boot 태그를 사용하시거나 관련 리포지터리에 pull request를 보내시면 됩니다. 링크를 아래에 안내해드리겠습니다.

추가로 강의에서 언급된 내용을 좀 더 상세히 살펴보고 싶으시다면 해당 강의자료를 다시 한 번 확인하시거나, 동시성 프로그래밍에 대한 전문 서적이나 자료를 참고하시기를 추천드립니다.

식빵님의 프로필 이미지
식빵

작성한 질문수

질문하기