如何“取消”倒计时Latch?

2022-09-02 20:30:20

我有多个消费者线程在大小为 1 的服务器上使用 。我有一个生产者线程,当它成功完成时调用。CountDownLatchawait()countDown()

当没有错误时,这很有效。

但是,如果生产者检测到错误,我希望它能够向使用者线程发出错误信号。理想情况下,我可以让生产者调用类似的东西,并让所有消费者收到中断异常或其他异常。我不想调用 ,因为这需要我所有的消费者线程在调用后进行额外的手动检查以确认成功。我宁愿他们只是收到一个异常,他们已经知道如何处理。abortCountDown()countDown()await()

我知道 中止设施不可用。是否有另一个同步原语,我可以轻松地适应以有效地创建支持中止倒计时的同步基元?CountDownLatchCountDownLatch


答案 1

JB Nizet有一个很好的答案。我拿起他的,稍微打磨了一下。结果是 CountDownLatch 的一个名为 AbortableCountDownLatch 的子类,它向该类添加了一个“abort()”方法,该方法将导致所有等待闩锁的线程接收 AbortException(InterruptedException 的子类)。

此外,与 JB 的类不同,AbortableCountDownLatch 将在中止时立即中止所有阻塞线程,而不是等待倒计时达到零(对于使用 count>1 的情况)。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class AbortableCountDownLatch extends CountDownLatch {
    protected boolean aborted = false;

    public AbortableCountDownLatch(int count) {
        super(count);
    }


   /**
     * Unblocks all threads waiting on this latch and cause them to receive an
     * AbortedException.  If the latch has already counted all the way down,
     * this method does nothing.
     */
    public void abort() {
        if( getCount()==0 )
            return;

        this.aborted = true;
        while(getCount()>0)
            countDown();
    }


    @Override
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
        final boolean rtrn = super.await(timeout,unit);
        if (aborted)
            throw new AbortedException();
        return rtrn;
    }

    @Override
    public void await() throws InterruptedException {
        super.await();
        if (aborted)
            throw new AbortedException();
    }


    public static class AbortedException extends InterruptedException {
        public AbortedException() {
        }

        public AbortedException(String detailMessage) {
            super(detailMessage);
        }
    }
}

答案 2

将此行为封装在特定的更高级别类中,并在内部使用 CountDownLatch:

public class MyLatch {
    private CountDownLatch latch;
    private boolean aborted;
    ...

    // called by consumers
    public void await() throws AbortedException {
        latch.await();
        if (aborted) {
            throw new AbortedException();
        }
    }

    // called by producer
    public void abort() {
        this.aborted = true;
        latch.countDown();
    }

    // called by producer
    public void succeed() {
        latch.countDown();
    }
}

推荐