Подтвердить что ты не робот

Почему AtomicBoolean не может заменить Boolean?

Oracle JDK Javadoc для AtomicBoolean:

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicBoolean.html

Логическое значение, которое может быть обновлено атомарно. См. Спецификация java.util.concurrent.atomic для описания свойства атомных переменных. AtomicBoolean используется в приложений, таких как атомарно обновляемые флаги, и не могут использоваться как замена для булева.

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

Это единственная причина или было что-то еще в виду, когда это было написано?

4b9b3361

Ответ 1

Boolean - это класс оболочки вокруг примитива Boolean. Он может автоматически создаваться с помощью Boolean компилятором (преобразование в бокс) или преобразовываться в логическое (unboxing conversion). Это не относится к AtomicBoolean, где это отдельный класс, предназначенный для целей concurrency.

Следовательно, два класса имеют различную семантику на уровне языка:

Boolean b = new Boolean(true);
AtomicBoolean ab = new AtomicBoolean(true);
System.out.println(true == b);  // automatic unboxing of Boolean variable
System.out.println(true == ab);  // compiler error

Ответ 2

Boolean - объект неизменяемого значения. Он был разработан как неизменный и окончательный, чтобы обеспечить его соблюдение. java.lang.Boolean существует с 1.0.

AtomicBoolean изменен и предназначен для обновления, чтобы обновленное значение отображалось в потоках. AtomicBoolean был представлен с Java 5.

Это совершенно разные понятия, поэтому AtomicBoolean не был предназначен для расширения Boolean. Вы не можете заменить изменяемый объект неизменным, не разрушая ожидаемые инварианты кода, используя его. Код, ожидающий получить неизменяемое значение, может быть сломан, если атомная версия может быть передана на свое место.

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

Атомные классы предназначены для работы с параллельными обновлениями (как улучшение на volatile), но наиболее эффективным способом разработки параллельного кода является использование неизменяемых значений. Поэтому будьте осторожны, чтобы не допускать AtomicBoolean для "логического, который вы используете при написании многопоточного кода".

Ответ 3

В одном случае используется AtomicBoolean не распространяется Boolean. Таким образом, если у вас есть метод, например:

void foo (Boolean b) {
    doStuff();
}

Затем вы не можете передать AtomicBoolean в качестве параметра foo. (Вам нужно будет вызвать метод get() AtomicBoolean.)

Ответ 4

Они не являются автоматически загружаемыми, поэтому их нельзя использовать в условных выражениях, например,

// Explodey
if (someAtomicBoolean) {
}

Ответ 5

Пример:

void doSomething( final Boolean flag ) {


  final boolean before = flag.booleanValue();

  do0( flag );

  final boolean after = flag.booleanValue();

  assert before == after;



  if ( flag.booleanValue() ) {
    do1();
  }

  if ( flag.booleanValue() ) {
    do2();
  }

}

может дать другой результат, чем

void doSomething( final AtomicBoolean flag ) {


  final boolean before = flag.get();

  do0( flag );

  final boolean after = flag.get();

  assert (before == after) || (before != after);



  if ( flag.get() ) {
    do1();
  }

  if ( flag.get() ) {
    do2();
  }

}

потому что AtomicBoolean может изменить свое значение, а Boolean не может.

В первом случае do1() и do2() либо оба вызываются, либо ни один из них.

Во втором случае оба, либо, либо ни одно из них не может быть вызвано, если значение AtomicBoolean изменяется одновременно.

Поскольку Boolean всегда был там и всегда был определен как неизменный, AtomicBoolean, который был введен намного позже, не может быть заменен для Boolean, потому что он ведет себя по-другому и код, который по праву полагается на неизменность Boolean может сломаться, если эта неизменяемость будет уничтожена.

Обратите внимание, что Boolean не может быть заменено на AtomicBoolean и наоборот. Они просто не совместимы в своей семантике.

Ответ 6

Потому что Boolean является неизменным. Смотрите: Почему класс Wrapper, такой как Boolean в java, неизменен? и мой ответ:


Потому что 2 равно 2. Завтра не будет 3.

Непрерывный всегда предпочтительнее по умолчанию, особенно в многопоточных ситуациях, и он упрощает чтение и сопровождение кода. Пример: API Java Date, который пронизан конструктивными недостатками. Если Date были неизменными, API был бы очень оптимизирован. Я бы знал, что операции Date будут создавать новые даты и никогда не будут искать API, которые их модифицируют.

Прочитайте Concurrency на практике, чтобы понять истинную важность неизменяемых типов.

Но также обратите внимание, что если по какой-то причине вы хотите использовать изменяемые типы, используйте AtomicInteger AtomicBoolean и т.д. Почему Atomic? Потому что, введя изменчивость, вы ввели необходимость обеспечения безопасности потоков. Что вам не понадобилось бы, если бы ваши типы оставались неизменными, поэтому при использовании изменяемых типов вы также должны заплатить цену за размышление о безопасности потоков и использовании типов из пакета concurrent. Добро пожаловать в чудесный мир параллельного программирования.

Кроме того, для Boolean - я призываю вас назвать одну операцию, которую вы, возможно, захотите выполнить, которая заботится о том, является ли Boolean изменчивым. установлено значение true? Используйте myBool = true. Это переназначение, а не мутация. Отрицание? myBool = !myBool. То же правило. Обратите внимание, что неизменяемость - это функция, а не ограничение, поэтому, если вы ее можете предложить, вы должны - и в этих случаях, конечно, можете.

Обратите внимание, что это относится и к другим типам. Самая тонкая вещь с целыми числами - count++, но это просто count = count + 1, если вы не хотите получать значение атомарно... в этом случае используйте изменяемый AtomicInteger.

Ответ 7

Первым основным вариантом использования атомных операций (иногда инкапсулированных) на любом языке является семантика сравнения и свопинга - фундаментальные строительные блоки параллельных приложений.

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

В Java Atomic * инкапсулируйте оба из вышеперечисленных, первое - с собственным кодом на основе платформы, а второе с помощью ключевого слова volatile.