Синхронизация с изменяемым логическим значением «флаг состояния»? - PullRequest
0 голосов
/ 10 июля 2019

Я читал о шаблоне «флага статуса» для использования volatile. Это говорит о том, что я могу использовать volatile без какой-либо синхронизации, если флаг состояния не зависит ни от какого другого состояния. Это гарантирует видимость флага для других потоков. Более того, запись в логическое значение является атомарной.

Но в другом вопросе сказано, что безопасно использовать volotile, когда только один поток может изменить флаг. В противном случае мне нужно использовать любую синхронизацию или AtomicBoolean.

В моем примере у меня есть флаг stopped, но его можно изменить более чем из одного потока: методы stop() и continue(). doSmth() не обновляет никакие состояния. Если предположить, что нормально не выполнять работу, когда stop() был вызван сразу после метода continue(), будет ли код безопасен для потока?

class MyClass {
    private volatile boolean stopped;

    public void doWork() {
        while(!sopeed) {
            doSmth();
        }
    }

    public void stop() {
        stopped = true;
    }

    public void continue() {
        stopped = false;
    }
}

Как по мне, так и должно быть. Не могли бы вы уточнить, если я ошибаюсь?

1 Ответ

0 голосов
/ 10 июля 2019

volatile просто гарантирует, что изменения в переменной доступны для всех потоков.

Фон: поток может создавать локальные копии общих переменных.Синхронизация значений этих локальных переменных с глобальными общими переменными - это то, что volatile влияет.

Однако это не синхронизирует в явном виде одной записи, монитора / критической области.

Весь набор инструментов java.util.concurrent предлагает такие вещи, как обеспечение того, что только один поток может изменить значение и тому подобное.Если вы хотите начать с нуля, можно с помощью двух переменных сделать некоторые блокирующие действия: поиск алгоритмов Дейкстры .

Здесь я думаю AtomicBoolean может подойти для неблокирующего использования.


Если вы хотите достичь глобального логического состояния, которое приостанавливает соотв. возобновляет потоков при переключении (ваш stopped) вместо некрасивого занятого ожидания :

public void run () {
    while (true) {
        doWork(); 
        try {
            barrier.await();
        } catch (InterruptedException | BrokenBarrierException ex) {
            return;
        }
    }
}

Использование глобального CyclicBarrier - не самый хороший API, поскольку он работает с N предопределенных Runnable s.

...