wait()가 항상 동기화된 블록에 있어야 하는 이유
호출하려면 이 콜을 동기화된 블록으로 발신해야 합니다.그렇지 않으면 가 느려집니다.그런데 이 제한을 두는 이유가 뭔가요?나는 그것을 알고 있습니다.wait()
모니터를 해방합니다만, 왜 특정 블록을 동기화하는 것에 의해서 모니터를 명시적으로 취득해, 다음에 콜을 발신하는 것에 의해서 모니터를 해방할 필요가 있는 것입니까.wait()
?
호출할 수 있는 경우 어떤 손상이 발생할 수 있습니까?wait()
동기화된 블록 밖에서 그 의미를 유지 - 발신자 스레드 일시 중단?
호출할 수 있는 경우 어떤 손상이 발생할 수 있습니까?
wait()
동기화된 블록 밖에서 그 의미를 유지 - 발신자 스레드 일시 중단?
이 경우 어떤 문제가 발생할지 설명하겠습니다.wait()
구체적인 예시와 함께 동기화된 블록 외부에 호출할 수 있습니다.
블로킹 큐를 실장한다고 가정합니다(API에는 이미1개가 있습니다).
첫 번째 시도(동기화되지 않음)는 아래 행에 따라 표시될 수 있습니다.
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
다음과 같은 일이 발생할 수 있습니다.
컨슈머 스레드가 호출한다.
take()
또, 그 것을 보면,buffer.isEmpty()
.컨슈머 스레드가 콜을 계속하기 전에
wait()
, 프로듀서 스레드가 등장하여 풀 호출give()
,그것은,buffer.add(data); notify();
이제 전기 소비 장치 스레드가 호출합니다.
wait()
(또한,notify()
호출된 지 얼마 안 됐어요).불운하면 생산자 스레드가 더 이상 생산되지 않습니다.
give()
전기 소비 장치가 깨지지 않고 데드록이 작동하기 때문입니다.
문제를 이해하면 해결 방법이 명확해집니다.사용하다synchronized
확실히 하다notify
사이에서는 호출되지 않습니다.isEmpty
그리고.wait
.
상세하게 설명하지 않고:이 동기화 문제는 보편적입니다.Michael Borgwardt가 지적한 것처럼 wait/notify는 스레드 간의 커뮤니케이션이기 때문에 항상 위와 같은 레이스 상태가 됩니다.따라서 "동기화된 내부에서만 대기" 규칙이 적용됩니다.
@Willie가 게시한 링크의 한 단락은 매우 잘 요약되어 있습니다.
당신은 웨이터와 통지자가 술어 상태에 대해 동의한다는 절대적인 보증이 필요합니다.웨이터는 잠들기 전에 술어의 상태를 약간 확인하지만, 취침 시 술어가 참인지에 따라 정확성이 달라집니다.이 두 사건 사이에는 취약성의 기간이 있으며, 이로 인해 프로그램이 중단될 수 있습니다.
생산자와 소비자가 합의할 필요가 있는 술어는 위의 예와 같다.buffer.isEmpty()
그리고 이 계약은 대기 및 통지가 확실하게 실행되도록 함으로써 해결됩니다.synchronized
블록.
이 투고는 다음 기사로 변경되었습니다.Java: 동기화된 블록에서 대기를 호출해야 하는 이유
A wait()
이치에 맞는 것은 역시 이치가 있는 경우뿐입니다.notify()
따라서 항상 스레드 간의 통신에 관한 것으로, 올바르게 동작하기 위해서는 동기화가 필요합니다.이는 암묵적이어야 하지만 다음과 같은 이유로 실제로 도움이 되지 않는다고 주장할 수 있다.
의미론적으로, 당신은 절대wait()
만족할 수 있는 조건이 필요합니다.그렇지 않으면 만족할 때까지 기다립니다.그러니까 진짜 하는 일은
if(!condition){
wait();
}
단, 이 조건은 다른 스레드로 설정되어 있기 때문에 올바르게 동작하기 위해서는 동기화가 필요합니다.
몇 가지 문제가 더 있는데, 기다리는 것을 그만뒀다고 해서 원하는 조건이 맞는 것은 아닙니다.
스플리어스 웨이크업(즉, 스레드가 알림을 수신하지 않고 대기 상태에서 웨이크업할 수 있음)이 발생할 수 있습니다.
조건은 설정할 수 있지만 대기 스레드가 웨이크업(모니터 재취득)될 때까지 세 번째 스레드가 조건을 다시 false로 만듭니다.
이러한 케이스에 대처하기 위해 필요한 것은 항상 다음과 같은 몇 가지 변형입니다.
synchronized(lock){
while(!condition){
lock.wait();
}
}
또한 동기화 프리미티브를 전혀 조작하지 않고 에서 제공하는 추상화를 사용하여 작업하는 것이 좋습니다.java.util.concurrent
패키지.
@ Rollerball의 말이 맞다.그wait()
이 경우 스레드가 어떤 상태가 발생할 때까지 대기할 수 있도록 호출됩니다.wait()
콜이 발생하면 스레드는 강제로 잠금을 포기합니다.
무언가를 포기하려면 먼저 소유해야 합니다.스레드가 먼저 잠금을 소유해야 합니다.따라서 내부로 호출할 필요가 있습니다.synchronized
메서드/블록.
네, 저는 위의 모든 답변에 동의합니다. 만약 당신이 아래의 조건을 체크하지 않았다면, 잠재적인 손상/불일관성에 관한 것입니다.synchronized
메서드/블록.그러나 @shrini1000이 지적한 바와 같이, 그냥 전화드렸습니다.wait()
동기화된 블록 내에서는 이 불일치가 발생하는 것을 막을 수 없습니다.
이전에 동기화하지 않으면 발생할 수 있는 문제wait()
는 다음과 같습니다.
- 첫 번째 스레드가 들어가는 경우
makeChangeOnX()
while 상태를 체크하면true
(x.metCondition()
돌아온다false
,수단x.condition
이false
)이 안에 들어갈 수 있도록 합니다.그럼 바로 전에wait()
메서드, 다른 스레드는 다음 주소로 이동합니다.setConditionToTrue()
를 설정합니다.x.condition
로.true
그리고.notifyAll()
. - 그 후에야 첫 번째 스레드가 그의 것으로 들어갑니다.
wait()
method (의 영향을 받지 않음)notifyAll()
몇 분 전에 일어난 일입니다).이 경우 첫 번째 스레드는 다른 스레드가 수행될 때까지 대기합니다.setConditionToTrue()
하지만 다시는 그런 일이 없을 수도 있습니다.
하지만 만약 당신이
synchronized
오브젝트 상태를 변경하는 메서드 전에는 이 작업이 수행되지 않습니다.
class A {
private Object X;
makeChangeOnX(){
while (! x.getCondition()){
wait();
}
// Do the change
}
setConditionToTrue(){
x.condition = true;
notifyAll();
}
setConditionToFalse(){
x.condition = false;
notifyAll();
}
bool getCondition(){
return x.condition;
}
}
wait() 메서드, notify() 메서드 및 notifyAll() 메서드가 스레드 간 통신에 사용되는 것은 모두 알고 있습니다.신호 누락 및 유사 웨이크업 문제를 제거하기 위해 대기 스레드는 항상 다음과 같은 특정 조건에서 대기합니다.
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
그런 다음 스레드 세트를 통지하는 was Notified 변수를 true로 설정하고 통지합니다.
모든 스레드에는 로컬 캐시가 있기 때문에 모든 변경 내용이 먼저 여기에 기록된 후 점차 메인 메모리로 승격됩니다.
이러한 메서드가 동기화된 블록 내에서 호출되지 않으면 wasNotified 변수는 메인메모리로 플래시되지 않고 스레드의 로컬캐시에 존재하기 때문에 대기 스레드는 스레드에 통지함으로써 리셋되었지만 신호를 계속 기다립니다.
이러한 유형의 문제를 해결하기 위해 동기화된 블록 내에서 항상 이러한 메서드가 호출됩니다.동기화된 블록이 시작되면 메인 메모리에서 모든 것이 읽혀지고 동기화된 블록을 종료하기 전에 메인 메모리에 플러시됩니다.
synchronized(monitor) {
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
}
고마워, 명확해졌으면 좋겠어.
이는 기본적으로 하드웨어 아키텍처(RAM 및 캐시)와 관련이 있습니다.
사용하지 않으면synchronized
와 함께wait()
또는notify()
모니터가 같은 블록에 들어갈 때까지 기다리는 대신 다른 스레드가 같은 블록에 들어갈 수 있습니다.게다가 동기 블록이 없는 어레이에 액세스 하는 경우, 다른 스레드에서는 그 변경이 인식되지 않는 경우가 있습니다.실제로 스레드 처리 CPU 코어의 x-레벨 캐시(예를 들어 첫 번째/2번째/3번째 레벨 캐시)에 어레이의 복사가 이미 있는 경우 다른 스레드는 변경을 인식하지 않습니다.
그러나 동기화된 블록은 메달의 한 면에 불과합니다.동기화되지 않은 컨텍스트에서 동기화된 컨텍스트 내의 개체에 실제로 액세스해도 개체의 자체 복사본을 캐시에 보관하기 때문에 동기화된 블록 내에서도 개체가 동기화되지 않습니다.저는 이 문제에 대해 https://stackoverflow.com/a/21462631에 썼습니다.잠금이 최종이 아닌 오브젝트를 보관하고 있을 때 오브젝트의 참조를 다른 스레드로 변경할 수 있습니까?
또한 대부분의 비복제 런타임 오류는 x-레벨 캐시가 원인이라고 확신합니다.이는 보통 개발자가 CPU의 작동 방식이나 메모리 계층이 애플리케이션 실행에 미치는 영향 등 낮은 수준의 내용을 학습하지 않기 때문입니다.http://en.wikipedia.org/wiki/Memory_hierarchy
프로그래밍 클래스가 메모리 계층과 CPU 아키텍처에서 먼저 시작되지 않는 이유는 여전히 수수께끼로 남아 있습니다."Hello world"는 여기엔 도움이 안 돼요;)
다음 문서에 따라주세요.
현재 스레드가 이 개체의 모니터를 소유해야 합니다.스레드는 이 모니터의 소유권을 해제합니다.
wait()
method는 단순히 객체에 대한 잠금을 해제하는 것을 의미합니다.따라서 오브젝트는 동기화된 블록/메서드 내에서만 잠깁니다.스레드가 동기화 블록 외부에 있는 경우 스레드가 잠겨 있지 않음을 의미하며, 스레드가 잠겨 있지 않은 경우 개체에 무엇을 놓습니까?
모니터링 개체(동기 블록에서 사용되는 개체)에 대한 스레드 대기. 단일 스레드의 전체 이동 경로에 n개의 모니터링 개체가 있을 수 있습니다.스레드가 동기화 블록 외부에서 대기하는 경우 모니터링 개체는 물론 모니터링 개체에 대한 액세스에 대한 다른 스레드도 알림이 없습니다.따라서 동기화 블록 외부의 스레드는 알림이 전달되었음을 어떻게 알 수 있습니까?이는 wait(), notify() 및 notifyAll()이 스레드클래스가 아닌 오브젝트클래스에 있는 이유 중 하나이기도 합니다.
기본적으로 모니터링 개체는 모든 스레드의 공통 리소스이며 모니터링 개체는 동기화 블록에서만 사용할 수 있습니다.
class A {
int a = 0;
//something......
public void add() {
synchronization(this) {
//this is your monitoring object and thread has to wait to gain lock on **this**
}
}
이 Java oracle 튜토리얼에서 직접 확인할 수 있습니다.
스레드가 d.wait를 호출하면 d에 대한 고유한 잠금을 소유해야 합니다. 그렇지 않으면 오류가 발생합니다.동기화된 메서드 내에서 wait를 호출하는 것은 본질적인 잠금을 취득하는 간단한 방법입니다.
전화할 때notify()
어떤 물건에 대해서t
, Java는 특정에 통지합니다.t.wait()
방법.단, 자바가 특정 특정 데이터를 검색하여 통지하는 방법은 무엇입니까?wait
방법.
Java는 객체에 의해 잠긴 동기화된 코드 블록만 조사합니다.t
. Java는 특정 코드를 통지하기 위해 전체 코드를 검색할 수 없습니다.t.wait()
.
언급URL : https://stackoverflow.com/questions/2779484/why-must-wait-always-be-in-synchronized-block
'programing' 카테고리의 다른 글
탈퇴 전환이 활성화되어 있는 동안 구성 요소 렌더링 계속 (0) | 2022.09.01 |
---|---|
Vuejs 비동기 데이터 함수의 중첩된 약속 (0) | 2022.09.01 |
InputStream에서 File 객체를 생성할 수 있습니까? (0) | 2022.09.01 |
C의 파일 설명자에서 파일 이름 검색 (0) | 2022.09.01 |
Nuxt JS/Firebase를 사용한 Vuex 미지의 액션 (0) | 2022.08.31 |