Ответ 1
Я не могу понять причину, по которой мы должны "никогда не синхронизироваться по логическим"
Вам нужно synchronize
для экземпляра постоянного объекта. Если вы синхронизированы на любом объекте, который вы назначаете (т.е. Меняете объект), тогда объект не является постоянным, а разные потоки будут синхронизироваться в разных экземплярах объектов. Поскольку они синхронизируются в разных объектных экземплярах, в этот же момент одновременно будут входить защищенные блоки, и условия гонки будут происходить. Это тот же ответ для синхронизации на 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
для всех объектов. Объект блокировки должен быть локальным для класса, в котором он используется, иначе вы будете блокировать один и тот же объект, который другие блоки могут блокировать в других случаях блокировки, если они совершают ту же ошибку.
Правильный шаблон, если вам нужно заблокировать логическое значение, заключается в определении объекта блокировки private final
:
private final Object lock = new Object();
...
synchronized (lock) {
...
Или вы также должны рассмотреть возможность использования объекта 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";
}
В вашем случае, поскольку похоже, что вам нужно переключать его вкл/выкл с помощью потоков, вам все равно нужно 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
будет доступен из других потоков, тогда он должен быть помечен как volatile
, если вы не будете synchronize
во время получения.