Java 클래스 라이브러리가 적합한 동기화 도구를 제공하지 않는 경우 사용자 정의 동기화 도구를 빌드해야 합니다.
상태 의존적 작업을 차단하는 구조
다음과 같이 코드 코드를 복사합니다.
객체 상태에 대한 잠금 획득;//잠금 획득 요청
while(전제조건이 성립하지 않음){//전제조건이 충족되지 않음
잠금 해제; //먼저 잠금을 해제합니다.
전제 조건이 유지될 때까지 기다립니다. // 전제 조건이 충족될 때까지 기다립니다.
중단되거나 시간 초과가 만료되면 선택적으로 실패합니다. // 중단 또는 시간 초과로 인해 실행이 실패했습니다.
reacquire lock;//잠금 획득을 다시 시도합니다.
}
작업 수행//실행
잠금해제; //잠금해제
제한된 캐시 구현 기본 클래스 예
다음과 같이 코드 코드를 복사합니다.
공개 클래스 BaseBoundBuffer<V> {
비공개 최종 V[] buf;
비공개 int 꼬리;
개인 정수 머리;
개인 정수 개수;
@SuppressWarnings("선택 해제됨")
공개 BaseBoundBuffer(정수 용량) {
buf = (V[]) 새로운 객체[용량];
}
공개 동기화 무효 doPut(V v) {
buf[tail] = v;
if (++tail == buf.length)
꼬리 = 0;
카운트++;
}
공개 동기화 V doTake() {
V v = buf[헤드];
if (++머리 == buf.length)
머리 = 0;
세다--;
v를 반환;
}
공개 최종 동기화 부울 isFull() {
반환 횟수 == buf.length;
}
공개 최종 동기화 부울 isEmpty() {
반환 횟수 == 0;
}
}
차단 구현 방법 1: 호출자에게 예외 발생
다음과 같이 코드 코드를 복사합니다.
공개 동기화 무효 put1(V v)에서 예외 발생{
if(isFull())
throw new Exception("전체 오류");
doPut(v);
}
분석: 예외가 발생할 때 예외를 사용해야 합니다. 여기서 예외를 발생시키는 것은 적절하지 않습니다. 호출자는 전제 조건이 실패한 상황을 처리해야 하며, 이는 근본적인 문제를 해결하지 못합니다.
차단 구현 방법 2: 폴링 및 절전을 통해
다음과 같이 코드 코드를 복사합니다.
public void put2(V v)가 InterruptedException을 발생시킵니다.
while (true) {//폴링
동기화됨 (이것) {
if (!isFull()) {
doPut(v);
반품;
}
}
Thread.sleep(SLEEP_TIME);//수면
}
}
분석: 수면 시간 SLEEP_TIME 설정에 가중치를 두는 것은 어렵습니다. 설정이 너무 작으면 CPU가 여러 번 폴링하여 더 많은 CPU 리소스를 소비할 수 있습니다. 설정이 너무 크면 응답성이 낮아집니다.
차단 구현 방법 3: 조건부 대기열
조건큐의 요소들은 관련 조건을 하나씩 기다리는 스레드들이다. 각 Java 객체는 잠금으로 사용될 수 있고, 각 객체는 조건 큐로도 사용될 수 있으며, Object의 wait,notify,notifyAll 메소드가 내부 조건 큐의 API를 구성합니다. Object.wait는 자동으로 잠금을 해제하고 다른 스레드가 잠금을 획득하고 개체 상태를 수정할 수 있도록 운영 체제에 현재 스레드를 일시 중단하도록 요청합니다. Object.notify 및 Object.notifyAll은 대기 중인 스레드를 깨울 수 있고, 조건 큐에서 스레드를 선택하여 깨운 후 잠금을 다시 획득하려고 시도할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
공개 동기화 무효 put3(V v)가 InterruptedException을 발생시킵니다.
동안(isFull())
기다리다();
도푸트(v);
통지모두();
}
분석: 간단하고 사용하기 쉬운 더 나은 응답을 얻으십시오.
조건부 대기열 사용
1. 조건부 술어
1).정의: 조건부 술어는 작업이 상태 종속 작업이 되기 위한 전제 조건입니다. 조건부 술어는 클래스의 개별 상태 변수로 구성된 표현식입니다. 예를 들어, put 메소드의 조건부 술어는 "캐시가 비어 있지 않습니다"입니다.
2) 관계: 조건부 대기에는 잠금, 대기 방법 및 조건부 술어를 포함하여 중요한 삼항 관계가 있습니다. 조건부 술어에는 여러 상태 변수가 포함되어 있으며 각 상태 변수는 잠금으로 보호되어야 하므로 조건부 술어를 테스트하기 전에 잠금을 유지해야 합니다. 잠금 객체와 조건부 대기열 객체(그리고 대기 및 알림 메서드가 호출되는 객체)는 동일한 객체여야 합니다.
3) 제약 조건: 대기할 각 호출은 특정 조건 조건과 암시적으로 연결됩니다. 특정 조건 조건이 호출되면 호출자는 이미 조건 대기열과 관련된 잠금을 보유해야 합니다. 상태 변수
2. 조건부 대기열 사용 규칙
1) 일반적으로 조건부 술어가 있습니다.
2).wait를 호출하기 전에 항상 조건부 조건을 테스트하고, wait에서 돌아온 후 다시 테스트하세요.
3).루프에서는 항상 대기를 호출하세요.
4) 조건 술어를 구성하는 상태 변수가 잠금으로 보호되는지 확인하고 이 잠금은 조건 대기열과 연결되어야 합니다.
5) wait, inform 및 informAll을 호출할 때 조건 대기열과 관련된 잠금을 유지해야 합니다.
6) 조건부 조건자를 확인한 후 보호된 논리 실행을 시작하기 전에 잠금을 해제하지 마십시오.
3.알림
nofify 대신 informAll을 사용해 보세요. nofify는 스레드를 휴면 상태에서 Blocked 상태로 무작위로 깨우기 때문입니다. (Blocked 상태는 항상 잠금을 획득하려고 시도하는 스레드입니다. 즉, 잠금이 사용 가능한 것으로 확인되면 잠금이 해제됩니다. 즉시 잠금을 유지하고,notifyAll 조건 큐에 있는 모든 스레드를 휴면 상태에서 Blocked 상태로 깨울 것입니다. 스레드 A가 조건 술어 Pa로 인해 휴면 상태에 들어가고 스레드 B가 휴면 상태에 들어가면 이 상황을 고려하십시오. 이때 조건 술어 Pb로 인해 휴면 상태에 들어갑니다. 이 참이면 스레드 C는 단일 알림을 실행합니다. JVM이 깨우기 위해 스레드 A를 무작위로 선택하면 스레드 A는 조건부 조건 Pa가 참이 아닌지 확인한 다음 이후에는 다른 스레드를 깨울 수 없습니다. 을 사용하면 프로그램은 항상 절전 상태가 됩니다. 통지All은 다릅니다. JVM은 조건 큐에 있는 모든 대기 스레드를 절전 상태에서 차단 상태로 깨웁니다. 조건 술어가 true가 아니기 때문에 스레드가 무작위로 선택되어 절전 상태에 들어가더라도 다른 스레드는 잠금을 위해 경쟁하고 계속 실행하세요.
4. 상태 의존성 방식의 표준형식 복사 코드는 다음과 같습니다.
void stateDependentMethod throwsInterruptedException{
동기화됨(잠금){
while(!conditionPredicate))
lock.wait();
}
//뭔가();
....
통지모두();
}
조건 개체 표시
명시적인 Condition 개체는 더 유연한 대안이며 더 풍부한 기능을 제공합니다. 각 잠금에 여러 개의 대기가 존재할 수 있고, 조건부 대기는 중단되거나 중단되지 않을 수 있으며, 시간 기반 대기, 공정하거나 불공평한 대기열 작업이 가능합니다. 조건 대기열이 내장 잠금과 연결되는 것처럼 조건은 잠금과 연결될 수 있습니다. 조건을 생성하려면 연관된 잠금에 대해 Lock.newCondition 메소드를 호출하십시오. 다음 코드는 표시 조건 변수를 사용하여 제한된 캐시를 다시 구현하는 데 사용됩니다.
공개 클래스 ConditionBoundedBuffer<V> {
비공개 최종 V[] buf;
비공개 int 꼬리;
개인 정수 머리;
개인 정수 개수;
개인 잠금 잠금 = 새로운 ReentrantLock();
개인 조건 notFullCondition = lock.newCondition();
개인 조건 notEmptyCondition = lock.newCondition();
@SuppressWarnings("선택 해제됨")
공공 ConditionBoundedBuffer(정수 용량) {
buf = (V[]) 새로운 객체[용량];
}
공공 무효 doPut(V v)는 InterruptedException을 발생시킵니다.
노력하다 {
lock.lock();
while (개수 == 버퍼 길이)
notFullCondition.await();
buf[tail] = v;
if (++tail == buf.length)
꼬리 = 0;
카운트++;
notEmptyCondition.signal();
} 마지막으로 {
lock.unlock();
}
}
공개 V doTake()가 InterruptedException을 발생시킵니다.
노력하다 {
lock.lock();
동안(개수 == 0)
notEmptyCondition.await();
V v = buf[헤드];
버프[헤드] = null;
if (++머리 == buf.length)
머리 = 0;
세다--;
notFullCondition.signal();
v를 반환;
} 마지막으로 {
lock.unlock();
}
}
}