Когда мне нужно использовать AtomicBoolean в Java?

Ответы:

245

Когда несколько потоков необходимо проверить и изменить логическое значение. Например:

if (!initialized) {
   initialize();
   initialized = true;
}

Это не потокобезопасно. Вы можете исправить это с помощью AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}
Bozho
источник
51
Это не похоже на реальный пример - другой поток может видеть, trueкогда он initialize()еще не завершен. Таким образом, это работает, только если другие потоки не заботятся о завершении initialize().
axtavt
6
@axtavt: Я думаю, что это вполне допустимый пример из реальной жизни, если initializedон просто используется для обеспечения того, чтобы один и только один поток вызывал initialize()метод. Очевидно, initializedчто истинность не означает, что инициализация определенно завершена в этом случае, поэтому, возможно, немного другой термин будет лучше здесь. Опять же, это зависит от того, для чего он используется.
ColinD
14
вам нужно 2 логических значения для initStarted и initCompleted, затем первый поток устанавливает initStarted и вызывает initialise (), остальные ждут, пока initCompleted станет true.
Мартин
3
@Bozho - чтение и запись в логические поля являются атомарными, верно ?, теперь volatile дает мне последнее значение логического поля. Так что, по сути, не было volatile booleanбы так же, как AtomicBoolean?
TheLostMind
2
@Martin: нет никакого прямого способа ждать, пока логическое значение не станет истинным; вам нужны дополнительные механизмы. Наиболее разумным подходом является использование synchronizedблока, в этом случае вам больше не нужен AtomicBoolean, просто a volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }будет гарантировать, что вызовет только один поток initialize, и что все остальные потоки ждут, пока он это initializedvolatile
сделает
52

Вот заметки (из книги Брайана Гетца ), которые я сделал, которые могут быть вам полезны

Классы AtomicXXX

  • обеспечить неблокирующую реализацию Compare-And-Swap

  • Использует аппаратную поддержку (инструкция CMPXCHG для Intel). Когда через ваш код выполняется множество потоков, использующих эти API-интерфейсы атомарного параллелизма, они будут масштабироваться намного лучше, чем код, использующий мониторы / синхронизацию на уровне объектов. Поскольку механизмы синхронизации Java заставляют код ждать, когда в критических секциях выполняется множество потоков, значительное количество процессорного времени затрачивается на управление самим механизмом синхронизации (ожидание, уведомление и т. Д.). Поскольку новый API использует конструкции аппаратного уровня (атомарные переменные) и алгоритмы ожидания и блокировки для реализации безопасности потоков, гораздо больше процессорного времени затрачивается на «выполнение задач», а не на управление синхронизацией.

  • они не только обеспечивают лучшую пропускную способность, но и обеспечивают большую устойчивость к таким проблемам, как тупик и инверсия приоритетов.

Аравинд Яррам
источник
34

Есть две основные причины, по которым вы можете использовать атомное логическое значение. Во-первых, он может быть изменён, вы можете передать его как ссылку и изменить значение, связанное, например, с самим логическим значением.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

и в классе someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Что еще более важно, его потокобезопасность и может указывать разработчикам, поддерживающим класс, что эта переменная, как ожидается, будет изменена и прочитана из нескольких потоков. Если вы не используете AtomicBoolean, вы должны синхронизировать булеву переменную, которую вы используете, объявив ее энергозависимой или синхронизируя чтение и запись поля.

Джон Винт
источник
4
Ради любви к Богу, это было только для того, чтобы показать изменчивость самого объекта. Я специально написал это для демонстрационных целей.
Джон Винт
И еще больше, если бы это было ВСЕ, что происходило, тогда да, это всегда вернет истину
Джон Винт
Это не доказывает, является ли это потокобезопасным или нет. Я могу закончить свои фрагменты кода, чтобы сделать класс очень потокобезопасным, но это только убивает мою точку зрения.
Джон Винт
1
Я думаю, что только Volatile недостаточно. Подумайте о ситуации, в которой два потока, которые читают и записывают одно и то же значение непосредственно из основной памяти, не синхронизируются между этими потоками - могут возникнуть проблемы параллелизма.
Шай Цадок
1
Вы правы, что для атомарного набора не хватило бы проверочных операций, хотя в ОП не было достаточно контекста, чтобы сделать такое предположение. Сказать, что изменчивости может быть недостаточно, всегда верно в зависимости от ситуации.
Джон Винт
18

AtomicBooleanКласс дает логическое значение , которое вы можете обновить атомарно. Используйте его, когда у нескольких потоков есть доступ к логической переменной.

Обзор пакета java.util.concurrent.atomic дает хорошее общее описание того, что делают классы в этом пакете и когда их использовать. Я бы также порекомендовал книгу Брайана Гетца « Практический параллелизм Java» .

Кэмерон Скиннер
источник
5

Выдержка из описания пакета

Пакет java.util.concurrent.atomic description: Небольшой инструментарий классов, поддерживающий безоблокировочное поточно-ориентированное программирование для отдельных переменных. [...]

Спецификации этих методов позволяют реализациям использовать эффективные атомарные инструкции машинного уровня, доступные на современных процессорах. [...]

Экземпляры классов AtomicBoolean, AtomicInteger, AtomicLong и AtomicReference каждый обеспечивают доступ и обновления к одной переменной соответствующего типа. [...]

Эффекты памяти для доступа и обновления атомных элементов обычно следуют правилам для летучих компонентов:

  • get имеет эффект памяти чтения изменчивой переменной.
  • set имеет эффект памяти записи (присваивания) переменной переменной.
  • weakCompareAndSet атомарно читает и условно записывает переменную, упорядочивается по отношению к другим операциям памяти с этой переменной, но в остальном действует как обычная операция энергонезависимой памяти.
  • CompareAndSet и все другие операции чтения и обновления, такие как getAndIncrement, имеют эффект памяти как чтения, так и записи изменчивых переменных.
OscarRyz
источник