Параллелизм Java: сбрасываемое условие включения / отключения ожидания - PullRequest
0 голосов
/ 22 ноября 2018

Я хочу дать потоку спать до тех пор, пока не выйдет определенное условие.В основном мне нужно три операции:

  • enable(): включить спящий режим (ничего не делать, если он уже включен)
  • disable(): отключить спящий режим (ничего не делать, если он уже отключен)
  • await(): дождитесь отключения спящего режима (или немедленного возврата, если спящий режим уже отключен) или прерывание потока (InterruptedException выброшено)

Сthis, thread A вызывает enable().Теперь поток B вызывает await() и переходит в режим сна, пока поток A (или другой) не вызовет disable().Этот цикл можно повторить.

Я знаю, что это довольно легко сделать с wait() и notify(), но мне интересно, есть ли в JDK8 такая функциональность?

БлижайшийЯ мог бы найти, что это CountdownLatch(1), к сожалению, реализация не может быть сброшена.

По сути, я просто хочу вызвать enable() / disable() и await(), в то время как все понятия параллелизма абстрагированы вреализация (хотя await() должен выдавать InterruptedException, что неизбежно).

Ответы [ 4 ]

0 голосов
/ 23 ноября 2018

Другая возможная реализация 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();
                }
            }
        }
    }
}
0 голосов
/ 22 ноября 2018
enable(): enable sleeping mode (do nothing if already enabled)
disable(): disable sleeping mode (do nothing if already disabled)

do nothing if already enabled (disabled) - это плохой дизайн, который может привести к тонким ошибкам, которые трудно воспроизвести и обнаружить.Например, пусть спящий режим отключен, и один поток вызывает disable(), а другой - enable().В зависимости от того, какой вызов сделан первым, режим будет оставаться включенным или отключенным навсегда.Чтобы сделать выполнение более детерминированным, должны учитываться включение и отключение, и будет определено (отключено) окончательное состояние.

Вместо этого ваши потоки должны обмениваться токенами , которые не маскируют друг друга.Помимо CountdownLatch (который фактически является счетчиком запретов), JDK имеет CyclicBarrier и Phaser, которые являются сбрасываемыми счетчиками запретов, и Semaphore, который является счетчиком разрешений.

UPDT эта реализация может работать (я не проверял это):

Phaser p = new Phaser(1);

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

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

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

N последовательных вызовов к enable() требуется одинаковое число disable() для прохождения ожидающего потока.

0 голосов
/ 23 ноября 2018

Вы также можете использовать 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();
    }


}
0 голосов
/ 22 ноября 2018

Вы можете использовать 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();
        }
    }
}
...