Почему не логично синхронизировать логику?

Мой архитектор всегда говорит, что

Никогда не синхронизируйтесь в Boolean

Я не могу понять причину, почему и действительно буду признателен, если бы кто-нибудь мог объяснить пример, почему это не очень хорошая практика. Код ссылочного образца

private Boolean isOn = false; private String statusMessage = "I'm off"; public void doSomeStuffAndToggleTheThing(){ // Do some stuff synchronized(isOn){ if(isOn){ isOn = false; statusMessage = "I'm off"; // Do everything else to turn the thing off } else { isOn = true; statusMessage = "I'm on"; // Do everything else to turn the thing on } } } 

Я не могу понять причину, по которой мы должны «никогда не синхронизироваться по логическим»

Вам нужно synchronize экземпляр постоянного объекта . Если вы синхронизировались на любом объекте, который вы назначаете (т. Е. Меняете объект), тогда объект не является постоянным, а разные streamи будут синхронизироваться в разных экземплярах объектов. Поскольку они синхронизируются в разных объектных экземплярах, в этот же момент одновременно будут входить защищенные блоки, и условия гонки будут происходить. Это тот же ответ для синхронизации по Long , Integer и т. Д.

 Boolean isOn; ... synchronized (isOn) { if (isOn) { // this changes the synchronized object isOn to another object // so another thread can then enter the synchronized with this thread isOn = false; 

Хуже того (как отметил в своем ответе @McDowell) любой Boolean , созданный с помощью autoboxing ( isOn = true ), является тем же объектом, что и Boolean.TRUE (или .FALSE ), который является одиночным ClassLoader в ClassLoader для всех объектов . Объект блокировки должен быть локальным для classа, в котором он используется, иначе вы будете блокировать один и тот же объект, который другие блоки могут блокировать в других случаях блокировки, если они совершают ту же ошибку.

Правильный шаблон, если вам нужно заблокировать логическое значение, состоит в том, чтобы определить private final объект блокировки:

 private final Object lock = new Object(); ... synchronized (lock) { ... 

Или вы также должны рассмотреть использование объекта AtomicBoolean что означает, что вам может вообще не AtomicBoolean synchronize его.

 private final AtomicBoolean isOn = new AtomicBoolean(false); ... // if it is set to false then set it to true, no synchronization needed if (isOn.compareAndSet(false, true)) { statusMessage = "I'm now on"; } else { // it was already on statusMessage = "I'm already on"; } 

В вашем случае, поскольку вам кажется, что вам нужно переключать его вкл / выкл с помощью streamов, вам все равно нужно будет synchronize объект lock и установить логическое значение и избежать условия проверки / установки гонки:

 synchronized (lock) { if (isOn) { isOn = false; statusMessage = "I'm off"; // Do everything else to turn the thing off } else { isOn = true; statusMessage = "I'm on"; // Do everything else to turn the thing on } } 

Наконец, если вы ожидаете, что statusMessage будет доступен из других streamов, тогда он должен быть отмечен как volatile если вы не будете synchronize во время получения.

 private Boolean isOn = false; public void doSomeStuffAndToggleTheThing(){ synchronized(isOn){ 

Это ужасная идея. isOn будет ссылаться на тот же объект, что и Boolean.FALSE который является общедоступным. Если любой другой fragment плохо написанного кода также решает заблокировать этот объект, две совершенно несвязанные транзакции должны будут ждать друг друга.

Замки выполняются на экземплярах объектов , а не на переменных, которые ссылаются на них:

введите описание изображения здесь

Я думаю, что ваша проблема больше связана с синхронизацией, чем с синхронизацией с булевыми. Представьте, что каждая нить – это дорога, где заявления (автомобили) идут один за другим. В какой-то момент может быть пересечение: без семафорных столкновений может случиться. Язык Java имеет встроенный способ описать это: поскольку любой объект может быть пересечением, любой объект имеет связанный монитор, действующий как семафор. Когда вы используете синхронизацию в своем коде, вы создаете семафор, поэтому вы должны использовать один и тот же для всех дорог (streamов). Таким образом, эта проблема не является действительно логической, поскольку существуют только два логических типа, эта проблема возникает каждый раз, когда вы синхронизируете переменную экземпляра, а затем указываете ту же переменную на другой объект. Таким образом, ваш код ошибочен с Booleans, но в равной степени опасен для целых чисел, строк и любого объекта, если вы не понимаете, что происходит.

Изменить: ответ Серый правильный.

Я хочу добавить: ваш архитектор прав, если с точки зрения Boolean неизменен , зачем его синхронизировать? Но multithreading сложна и основана на сценарии.

  • Синхронизация по целочисленному значению
  • Как сделать блокировку с несколькими чтениями / одиночной записью из более простых примитивов синхронизации?
  • Связаны ли в LinkedBlockingQueue методы удаления и удаления?
  • Можно ли использовать мьютекс в многопроцессорном случае в Linux / UNIX?
  • Как синхронизированные статические методы работают на Java?
  • Синхронизация IPC с общей памятью (без блокировки)
  • Синхронизировано ли унаследовано в Java?
  • Явная блокировка Java
  • Каковы различия между различными параметрами синхронизации streamов в C #?
  • Синхронный запрос в Node.js
  • Убедитесь, что синхронизированные блокировки Java выполнены в порядке?
  • Давайте будем гением компьютера.