简体   繁体   English

Java并发:resettable启用/禁用等待条件

[英]Java concurrency: resettable enabled/disabled wait condition

I want to let a thread sleep until a certain condition becomes off. 我想让一个线程睡眠,直到某个条件关闭。 Basically, I need three operations: 基本上,我需要三个操作:

  • enable() : enable sleeping mode (do nothing if already enabled) enable() :启用睡眠模式(如果已启用则不执行任何操作)
  • disable() : disable sleeping mode (do nothing if already disabled) disable() :禁用睡眠模式(如果已禁用则不执行任何操作)
  • await() : wait until sleeping mode becomes disabled (or return immediately if the sleeping mode was already disabled) or the thread becomes interrupted ( InterruptedException is thrown) await() :等待睡眠模式被禁用(或者如果睡眠模式已被禁用则立即返回)或者线程被中断(抛出InterruptedException

With this, thread A calls enable() . 有了这个,线程A调用enable() Now thread B calls await() and goes to sleep until thread A (or another one) calls disable() . 现在,线程B调用await()并进入休眠状态,直到线程A (或另一个)调用disable() This cycle can be repeated. 可以重复该循环。

I know this can be quite easily done with wait() and notify() , but I am wondering if JDK8 has such functionality built-in? 我知道这可以通过wait()notify()轻松完成,但我想知道JDK8是否内置了这样的功能?

The closest I could find is was a CountdownLatch(1) , unfortunately the implementation is not resettable. 我能找到的最接近的是CountdownLatch(1) ,遗憾的是实现不可重置。

Basically, I just want to call enable() / disable() and await() , while all concurrency concepts are abstracted in the implementation (though await() should throw InterruptedException , which is unavoidable). 基本上,我只想调用enable() / disable()await() ,而所有并发概念都在实现中被抽象(尽管await()应该抛出InterruptedException ,这是不可避免的)。

You could use a Semaphor too : 您也可以使用Semaphor

import java.util.concurrent.Semaphore;

public class Switch {

    private Semaphore semaphore = new Semaphore(1);

    public void enable() {
        synchronized(this) {
            semaphore.drainPermits(); // 0
            semaphore.reducePermits(1); // -1 or 0
        }
    }

    public void disable() {
        semaphore.release(2); // 1 or 2
    }

    public void await() throws InterruptedException {
        semaphore.acquire();
        semaphore.release();
    }


}

Another possible implementation of Switch : Switch另一种可能实现:

public class Switch {
    private final AtomicBoolean state = new AtomicBoolean();

    public void enable() {
        state.set(true);
    }

    public void disable() {
        if (state.compareAndSet(true, false)) {
            synchronized (state) {
                state.notifyAll();
            }
        }
    }

    public void await() throws InterruptedException {
        if (state.get()) {
            synchronized (state) {
                while (state.get()) {
                    state.wait();
                }
            }
        }
    }
}

You could use Condition : 你可以使用Condition

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Switch {
    private final Lock lock = new ReentrantLock();
    private final Condition on = lock.newCondition();
    private final Condition off = lock.newCondition();
    private volatile boolean state = true; 

    public void enable() {
        try {
            lock.lock();
            state = true;
            on.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void disable() {
        try {
            lock.lock();
            state = false;
            off.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void await() {
        try {
            lock.lock();
            while(!state) {
                try {
                    off.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException("waiting interrupted.");
                }
            }
        } finally {
            lock.unlock();
        }
    }
}
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)

do nothing if already enabled (disabled) is a bad design, which can lead to subtle bugs which are hard to reproduce and discover. do nothing if already enabled (disabled)是一个糟糕的设计,可能会导致细微的错误,难以重现和发现。 For example, let sleeping mode is disabled, and one thread calls disable() and the other calls enable() . 例如,禁用休眠模式,一个线程调用disable() ,另一个调用enable() Depending on which call is made first, the mode will stay enabled or disabled forever. 根据首先进行的呼叫,模式将永久保持启用或禁用。 To make execution more deterministic, enabling and disabling must be counted, and the final state will be determined (disabled). 为了使执行更具确定性,必须计算启用和禁用,并确定(禁用)最终状态。

Instead, your threads should exchange tokens which do not mask each other. 相反,你的线程应该交换不相互掩盖的令牌 Besides CountdownLatch , (which effectively is a counter of prohibitions), JDK has CyclicBarrier and Phaser , which are resettable counters of prohibitions, and Semaphore , which is a counter of permissions. 除了CountdownLatch (实际上是一个禁止的计数器),JDK还有CyclicBarrierPhaser ,它们是可重置的禁止计数器,而Semaphore是一个权限计数器。

UPDT this implementation may work (I did not tested it): UPDT这个实现可能有用(我没有测试过):

Phaser p = new Phaser(1);

public void await() {
    p.arriveAndAwaitAdvance();
}

public void enable() {
    p.register();
}

public void disable() {
    p.arriveAndDeregister();
}

N sequential calls to enable() require the same number of disable() to pass the awaiting thread. enable() N次连续调用需要相同数量的disable()来传递等待线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM