2020 Java 동시 및 다중 스레드 자습서(19): 파이프라인 잠금 해제
16815 단어 #Java 동시 자습서 2020 버전
문서 목록
파이프를 끼워 넣는 것은 자물쇠가 죽는 것과 유사하다.네스트된 파이프 스레드 잠금이 이렇게 발생합니다.
Thread 1 synchronizes on A
Thread 1 synchronizes on B (while synchronized on A)
Thread 1 decides to wait for a signal from another thread before continuing
Thread 1 calls B.wait() thereby releasing the lock on B, but not A.
Thread 2 needs to lock both A and B (in that sequence)
to send Thread 1 the signal.
Thread 2 cannot lock A, since Thread 1 still holds the lock on A.
Thread 2 remain blocked indefinately waiting for Thread1
to release the lock on A
Thread 1 remain blocked indefinately waiting for the signal from
Thread 2, thereby
never releasing the lock on A, that must be released to make
it possible for Thread 2 to send the signal to Thread 1, etc.
이것은 이론적인 상황처럼 들리지만 다음 Lock의 직접적인 실현을 보십시오.
// lock
public class Lock{
protected MonitorObject monitorObject = new MonitorObject();
protected boolean isLocked = false;
public void lock() throws InterruptedException{
synchronized(this){
while(isLocked){
synchronized(this.monitorObject){
this.monitorObject.wait();
}
}
isLocked = true;
}
}
public void unlock(){
synchronized(this){
this.isLocked = false;
synchronized(this.monitorObject){
this.monitorObject.notify();
}
}
}
}
lock () 방법은 "this"에서 동기화한 다음 모니터 Object 구성원 변수에서 동기화하는 것을 주의하십시오.isLocked가 false인 경우 문제가 없습니다.이 스레드는 모니터Object를 호출하지 않습니다.wait(). 단, isLocked가true라면, lock () 를 호출하는 루트는 모니터 Object에 멈추게 됩니다.wait () 호출 중입니다.
문제는 모니터 Object를 호출하는 것입니다.wait () 는 모니터 Object 구성원의 동기화 프로세스만 방출하고'this'와 관련된 동기화 프로세스는 방출하지 않습니다.다시 말하면 대기 상태에 멈춘 라인은'this'의 동기화 자물쇠를 가지고 있다.
Lock을 잠근 첫 번째 스레드가 unlock () 을 호출하여 잠금을 해제하려고 시도할 때, unlock () 방법의 synchronized (this) 코드 블록에 들어가려고 시도할 때 막힙니다.lock () 에서 기다리는 라인이synchronized (this) 를 떠날 때까지 차단 상태를 유지합니다.단, lock () 방법에서 기다리는 루틴은 isLocked가false로 설정되고 모니터Object를 실행할 때까지 이 블록을 떠나지 않습니다.notify (), 이것이 바로 unlock () 입니다.
간단히 말하면, lock () 를 기다리는 라인은 lock () 과 동기화 블록을 종료하기 위해 unlock () 를 성공적으로 실행해야 한다.단, lock () 에서 기다리는 루틴이 외부 동기화 블록을 떠나기 전에는 unlock () 을 실행할 수 있는 루틴이 없습니다.
결과적으로, lock () 또는 unlock () 를 호출하는 모든 라인이 영원히 막힐 것입니다.이것은 파이프를 끼워 넣는 자물쇠사라고 부른다.
더욱 현실적인 예
너는 영원히 위의 방식대로 자물쇠를 실현하지 못할 것이라고 말할 수 있다.내부 관리 대상에서wait()와 notify()를 호출하지 않고this에서 호출하기 때문이다.그럴 가능성이 높습니다.그러나 어떤 경우 상술한 디자인이 나타날 수 있다.예를 들어 Lock에서 공평성을 실현하려면이렇게 할 때, 한 번에 한 라인을 알릴 수 있도록 각 라인이 각자의 대기열 대상에서wait () 를 호출하기를 바랍니다.
이런 공평한 자물쇠의 간단한 실현을 살펴보자.
//Fair Lock implementation with nested monitor lockout problem
public class FairLock {
private boolean isLocked = false;
private Thread lockingThread = null;
private List<QueueObject> waitingThreads =
new ArrayList<QueueObject>();
public void lock() throws InterruptedException{
QueueObject queueObject = new QueueObject();
synchronized(this){
waitingThreads.add(queueObject);
while(isLocked || waitingThreads.get(0) != queueObject){
synchronized(queueObject){
try{
queueObject.wait();
}catch(InterruptedException e){
waitingThreads.remove(queueObject);
throw e;
}
}
}
waitingThreads.remove(queueObject);
isLocked = true;
lockingThread = Thread.currentThread();
}
}
public synchronized void unlock(){
if(this.lockingThread != Thread.currentThread()){
throw new IllegalMonitorStateException(
"Calling thread has not locked this lock");
}
isLocked = false;
lockingThread = null;
if(waitingThreads.size() > 0){
QueueObject queueObject = waitingThreads.get(0);
synchronized(queueObject){
queueObject.notify();
}
}
}
}
public class QueueObject {}
언뜻 보기에는 이 실현이 괜찮은 것 같지만, lock () 방법은queueObject를 호출했습니다.wait (), 이 호출은 두 동기화 블록 내부에 있습니다.하나는 "this"에서 동기화되고, 다른 하나는 그 안에 끼워 넣고queueObject 국부 변수에서 동기화됩니다.스레드가 queueObject를 호출할 때.wait ()일 때 QueueObject 실례의 자물쇠를 방출하지만 "this"와 관련된 자물쇠는 방출하지 않습니다.
또한 unlock () 방법은synchronized로 성명되며, 이것은synchronized (this) 블록에 해당합니다.이것은 lock () 에서 라인이 기다리면'this' 와 관련된 파이프 대상이 이 기다린 라인에 잠긴다는 것을 의미합니다.unlock () 를 호출하는 모든 루틴은 'this' 에 대한 잠금이 풀리기를 기다립니다.그러나 이것은 영원히 발생하지 않을 것이다. 라인이 기다리는 라인에 신호를 성공적으로 보낼 때만 발생하고, 신호를 보내는 방법은 unlock () 방법을 통해서만 실행될 수 있기 때문이다.
따라서 위의 FairLock이 실현되면 파이프라인 잠금이 끊어질 수 있습니다.'배고픔과 공평성'편에서 공평의 자물쇠를 어떻게 더 잘 실현할 수 있는지 설명했다.
파이프라인 잠금 죽기 vs 잠금 죽기
파이프를 끼워 넣은 자물쇠가 죽은 것과 죽은 자물쇠의 결과는 거의 같다. 관련된 루트는 결국 막혀서 영원히 상대방을 기다린다.
그러나 이 두 가지 상황은 결코 등가가 아니다.사라진 자물쇠 편에서 말한 바와 같이 두 라인이 서로 다른 순서로 자물쇠를 얻었을 때 사라진 자물쇠가 발생한다.스레드 1 잠금 A, B를 기다립니다.스레드 2에서 B를 잠그고 A를 기다립니다.잠금 방지 편에서 말한 바와 같이 항상 같은 순서로 잠금(잠금 순서)을 하면 잠금이 사라지는 것을 피할 수 있다.그러나 플러그 튜브 잠금 사망은 두 라인이 같은 순서로 잠금되어 발생하는 것이다.스레드 1은 A와 B를 잠그고 B를 방출하고 스레드 2의 신호를 기다린다.스레드 2는 A와 B가 동시에 있어야만 스레드 1에 신호를 보낼 수 있다.결과적으로 한 라인은 신호를 기다리고 다른 라인은 자물쇠를 풀기를 기다리고 있다.
차이점은 다음과 같습니다.
에피소드를 번역하다.
원문: At first glance this implementation may look fine, but notice how the lock() method calls queue Object.wait(); from inside two synchronized blocks.
설명: "notice how"는 "xxx가 어떻게 되는지 주의"로 쉽게 번역할 수 있지만, 사실은 그렇지 않다.이곳의'how'는'어떻게'가 아니라 안내어이며 목적어 종문을 이어받는다.그러니까 직역할 필요 없어.
번역문: 언뜻 보기에는 이 실현이 괜찮은 것 같지만, lock () 방법은queueObject를 호출했습니다.wait (), 이 호출은 두 동기화 블록 내부에 있습니다.
다음: 2020 버전 자바 병렬 및 다중 스레드 튜토리얼 (20): 슬라이딩 조건
동시 사용 칼럼: Java 동시 사용 및 멀티스레드 자습서 2020 버전
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Rails Turbolinks를 페이지 단위로 비활성화하는 방법원래 Turobolinks란? Turbolinks는 링크를 생성하는 요소인 a 요소의 클릭을 후크로 하고, 이동한 페이지를 Ajax에서 가져옵니다. 그 후, 취득 페이지의 데이터가 천이 전의 페이지와 동일한 것이 있...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.