sourcecode

자바에는 '조건이 true가 될 때까지 차단' 함수가 있습니까?

copyscript 2022. 9. 18. 10:03
반응형

자바에는 '조건이 true가 될 때까지 차단' 함수가 있습니까?

서버용 리스너 스레드를 쓰고 있으며, 현재 사용하고 있는 것은 다음과 같습니다.

while (true){
    try {
        if (condition){
            //do something
            condition=false;
        }
        sleep(1000);

    } catch (InterruptedException ex){
        Logger.getLogger(server.class.getName()).log(Level.SEVERE, null, ex);
    }
}

위의 코드를 사용하면 실행 기능이 CPU 타임 루프를 모두 소비하는 문제가 발생합니다.수면 기능은 작동하지만 임시방편일 뿐 해결책이 아닌 것 같습니다.

변수 'condition'이 'true'가 될 때까지 차단하는 함수가 있습니까?또는 변수의 값이 변경될 때까지 대기하는 표준 방법이 연속 루프입니까?

이와 같은 여론조사는 확실히 가장 선호도가 낮은 해결책입니다.

이 조건을 실현하기 위해 뭔가 할 수 있는 또 다른 실이 있을 거라고 생각합니다.스레드를 동기화하는 방법은 여러 가지가 있습니다.이 경우 오브젝트를 통한 알림이 가장 쉽습니다.

메인 스레드:

synchronized(syncObject) {
    try {
        // Calling wait() will block this thread until another thread
        // calls notify() on the object.
        syncObject.wait();
    } catch (InterruptedException e) {
        // Happens if someone interrupts your thread.
    }
}

기타 스레드:

// Do something
// If the condition is true, do the following:
synchronized(syncObject) {
    syncObject.notify();
}

syncObject 할 수 Object.

스레드 간 통신에는 그 밖에도 여러 가지 방법이 있지만 어떤 방법을 사용할지는 정확히 무엇을 하고 있는지에 따라 달라집니다.

EboMike 대답과 Toby의 대답은 둘 다 옳은 방향으로 가고 있지만 둘 다 치명적인 결함을 가지고 있다. 결함을 Loss Notification이라고 합니다.

가 콜을 하는 foo.notify() 슬리브, , 슬리브, 슬리브, 슬리브, 슬리브, 슬리브, 슬리브에 잠들어 않으면 할수 없습니다foo.wait(). " ". "foo는 통지된 것을 기억하지 못합니다.

를 걸 수 foo.wait() ★★★★★★★★★★★★★★★★★」foo.notify()후우이는 알림 손실을 방지하는 유일한 방법은 뮤텍스로 상태를 보호하는 것이기 때문입니다.올바르게 처리되면 다음과 같이 됩니다.

컨슈머 스레드:

try {
    synchronized(foo) {
        while(! conditionIsTrue()) {
            foo.wait();
        }
        doSomethingThatRequiresConditionToBeTrue();
    }
} catch (InterruptedException e) {
    handleInterruption();
}

프로듀서 스레드:

synchronized(foo) {
    doSomethingThatMakesConditionTrue();
    foo.notify();
}

조건을 변경하는 코드와 조건을 체크하는 코드는 모두 같은 오브젝트에서 동기화되며 컨슈머 스레드는 대기하기 전에 조건을 명시적으로 테스트합니다. '알림'에 되는 .wait()를 호출합니다.

, 「」는,wait()루프를 하고 있습니다.이면, 재취득할 수 있기 때문입니다foo다른 스레드가 이 상태를 다시 false로 만들었을 수 있습니다.사용하시는 프로그램에서는 불가능해도 일부 운영체제에서는 가능한 것은foo.wait()foo.notify()가 호출되지 않았습니다.이를 스플리어스 웨이크업이라고 합니다.이러한 웨이크업은 특정 운영체제에서 구현하기 쉽게 하기 때문에 발생할 수 있습니다.

아무도 Count Down Latch를 사용하여 솔루션을 공개하지 않았기 때문입니다.그럼 어떻게 되는 거죠?

public class Lockeable {
    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    public void doAfterEvent(){
        countDownLatch.await();
        doSomething();
    }

    public void reportDetonatingEvent(){
        countDownLatch.countDown();
    }
}

로 waitnotifyEboMike의/notify/notify와 유사한 메커니즘을 사용할 수 . 있습니다.Lock.

예를들면,

public void doSomething() throws InterruptedException {
    lock.lock();
    try {
        condition.await(); // releases lock and waits until doSomethingElse is called
    } finally {
        lock.unlock();
    }
}

public void doSomethingElse() {
    lock.lock();
    try {
        condition.signal();
    } finally {
        lock.unlock();
    }
}

다른 스레드에 의해 통지되는 상태를 기다리는 장소(이 경우 호출)doSomethingElse이 시점에서 첫 번째 스레드는 계속됩니다.

「」를 사용합니다.Lock는 단지 인 s를 이다.Condition상태를 나타내는 오브젝트(producer-timeout 등 여러 가지 조작을 할 수 있습니다).

또한, 저는 당신의 예에서 중단되는 예외를 어떻게 다루는지 주목하지 않을 수 없습니다..대신 다음 명령을 사용하여 인터럽트 상태 플래그를 리셋합니다.Thread.currentThread().interrupt

이는 예외가 느려지면 인터럽트 상태 플래그가 리셋되고('인터럽트를 받은 기억이 없습니다.다른 사람에게 물어보면사실을 알릴없습니다'라는 의미) 다른 프로세스가 이 질문에 의존할 수 있기 때문입니다.예를 들어 다른 무언가가 이를 기반으로 중단 정책을 구현한 경우...휴. 또 다른 예는 당신의 중단 정책이 오히려while(true)되었을지도 while(!Thread.currentThread().isInterrupted() 배려가있다사회적 배려가 있다).

요약하자면, '우리'를 Conditionwait//weat/weat/weat를 하는 것과 ., 「」를 선택합니다.LockInterruptedException

세마포어도 쓸 수 있어

조건이 충족되지 않으면 다른 스레드가 세마포를 획득합니다.
가 그것을 할 입니다.acquireUninterruptibly()
★★★★★★★★★★★★★★★★★」tryAcquire(int permits, long timeout, TimeUnit unit)차단될 수 있습니다.

조건이 충족되면 세마포도 해제되고 스레드가 세마포를 획득합니다.

또는 을 사용해 볼 수도 있습니다.

록프리 솔루션(?)

저도 같은 문제가 있었습니다만, 잠금을 사용하지 않는 솔루션을 원했습니다.

문제:큐에서 소비하는 스레드는 최대 1개입니다.여러 생산자 스레드가 항상 큐에 삽입되며 대기 중인 경우 소비자에게 알려야 합니다.큐에는 잠금이 없기 때문에 알림에 잠금을 사용하면 프로듀서 스레드에서 불필요한 차단이 발생합니다.각 생산자 스레드는 대기 중인 소비자에게 통지하기 전에 잠금을 획득해야 합니다.JDK에 록프리 장벽이 있으면 찾을 수 없습니다.둘다요.CyclicBarrier ★★★★★★★★★★★★★★★★★」CoundDownLatch내부적으로 자물쇠를 사용했어요

이것은 나의 약간 축약된 코드이다.이 코드는 한 번에 하나의 스레드만 대기할 수 있습니다.여러 소유자를 저장하기 위해 원자 수집 유형을 사용하여 여러 대기자/소비자를 허용하도록 수정할 수 있습니다(a).ConcurrentMap동작할 수 있습니다.

이 코드를 사용했는데 작동되는 것 같아요.나는 그것을 광범위하게 테스트하지 않았다.사용 전에 설명서를 읽어 보시기 바랍니다.

/* I release this code into the public domain.
 * http://unlicense.org/UNLICENSE
 */

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;

/**
 * A simple barrier for awaiting a signal.
 * Only one thread at a time may await the signal.
 */
public class SignalBarrier {
    /**
     * The Thread that is currently awaiting the signal.
     * !!! Don't call this directly !!!
     */
    @SuppressWarnings("unused")
    private volatile Thread _owner;

    /** Used to update the owner atomically */
    private static final AtomicReferenceFieldUpdater<SignalBarrier, Thread> ownerAccess =
        AtomicReferenceFieldUpdater.newUpdater(SignalBarrier.class, Thread.class, "_owner");

    /** Create a new SignalBarrier without an owner. */
    public SignalBarrier() {
        _owner = null;
    }

    /**
     * Signal the owner that the barrier is ready.
     * This has no effect if the SignalBarrer is unowned.
     */
    public void signal() {
        // Remove the current owner of this barrier.
        Thread t = ownerAccess.getAndSet(this, null);

        // If the owner wasn't null, unpark it.
        if (t != null) {
            LockSupport.unpark(t);
        }
    }

    /**
     * Claim the SignalBarrier and block until signaled.
     *
     * @throws IllegalStateException If the SignalBarrier already has an owner.
     * @throws InterruptedException If the thread is interrupted while waiting.
     */
    public void await() throws InterruptedException {
        // Get the thread that would like to await the signal.
        Thread t = Thread.currentThread();

        // If a thread is attempting to await, the current owner should be null.
        if (!ownerAccess.compareAndSet(this, null, t)) {
            throw new IllegalStateException("A second thread tried to acquire a signal barrier that is already owned.");
        }

        // The current thread has taken ownership of this barrier.
        // Park the current thread until the signal. Record this
        // signal barrier as the 'blocker'.
        LockSupport.park(this);
        // If a thread has called #signal() the owner should already be null.
        // However the documentation for LockSupport.unpark makes it clear that
        // threads can wake up for absolutely no reason. Do a compare and set
        // to make sure we don't wipe out a new owner, keeping in mind that only
        // thread should be awaiting at any given moment!
        ownerAccess.compareAndSet(this, t, null);

        // Check to see if we've been unparked because of a thread interrupt.
        if (t.isInterrupted())
            throw new InterruptedException();
    }

    /**
     * Claim the SignalBarrier and block until signaled or the timeout expires.
     *
     * @throws IllegalStateException If the SignalBarrier already has an owner.
     * @throws InterruptedException If the thread is interrupted while waiting.
     *
     * @param timeout The timeout duration in nanoseconds.
     * @return The timeout minus the number of nanoseconds that passed while waiting.
     */
    public long awaitNanos(long timeout) throws InterruptedException {
        if (timeout <= 0)
            return 0;
        // Get the thread that would like to await the signal.
        Thread t = Thread.currentThread();

        // If a thread is attempting to await, the current owner should be null.
        if (!ownerAccess.compareAndSet(this, null, t)) {
            throw new IllegalStateException("A second thread tried to acquire a signal barrier is already owned.");
        }

        // The current thread owns this barrier.
        // Park the current thread until the signal. Record this
        // signal barrier as the 'blocker'.
        // Time the park.
        long start = System.nanoTime();
        LockSupport.parkNanos(this, timeout);
        ownerAccess.compareAndSet(this, t, null);
        long stop = System.nanoTime();

        // Check to see if we've been unparked because of a thread interrupt.
        if (t.isInterrupted())
            throw new InterruptedException();

        // Return the number of nanoseconds left in the timeout after what we
        // just waited.
        return Math.max(timeout - stop + start, 0L);
    }
}

사용 방법의 모호한 예를 들자면, James Large의 예를 들겠습니다.

SignalBarrier barrier = new SignalBarrier();

컨슈머 스레드(복수가 아닌 단일!):

try {
    while(!conditionIsTrue()) {
        barrier.await();
    }
    doSomethingThatRequiresConditionToBeTrue();
} catch (InterruptedException e) {
    handleInterruption();
}

생산자 스레드:

doSomethingThatMakesConditionTrue();
barrier.signal();

,, IT 할 수 .CompletableFuture8 ):s(Java 8 이)):

final CompletableFuture<String> question = new CompletableFuture<>();

// from within the consumer thread:
final String answer = question.get(); // or: event.get(7500000, TimeUnit.YEARS)

// from within the producer thread:
question.complete("42");

언급URL : https://stackoverflow.com/questions/5999100/is-there-a-block-until-condition-becomes-true-function-in-java

반응형