为什么在布尔值上进行同步不是一个好的做法?

2022-09-01 03:33:05

我的建筑师总是说

从不在布尔值上同步

我无法理解原因,如果有人能用一个例子解释为什么这不是一个好的做法,我将不胜感激。参考示例代码

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
      }
   }
}

答案 1

我无法理解为什么我们应该“永远不要在布尔值上同步”

应始终位于常量对象实例上。如果您在要分配的任何对象上进行同步(即将对象更改为新对象),则它不是恒定的,并且不同的线程将在不同的对象实例上进行同步。由于它们在不同的对象实例上同步,因此多个线程将同时进入受保护的块,并且将发生争用情况。这与 同步 上 、等的答案相同。synchronizeLongInteger

// this is not final so it might reference different objects
Boolean isOn = true;
...
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;

更糟糕的是,通过自动装箱 () 创建的任何内容都与 (或 ) 是同一对象,后者是所有对象中的单例。您的 lock 对象应该是使用它的类的本地对象,否则,如果其他类犯了相同的错误,您将锁定在其他锁定情况下可能锁定的同一单例对象。BooleanisOn = trueBoolean.TRUE.FALSEClassLoader

如果您需要在布尔值周围锁定,正确的模式是定义一个锁定对象:private final

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

synchronized (lock) {
   ...

或者,您还应该考虑使用该对象,这意味着您可能根本不需要使用它。AtomicBooleansynchronize

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";
}

在你的例子中,由于看起来你需要用线程打开/关闭它,那么你仍然需要在对象上设置布尔值并避免测试/设置争用条件:synchronizelock

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
    }
}

最后,如果您希望从其他线程访问,则应将其标记为,除非您在get期间也这样做。statusMessagevolatilesynchronize


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

这是一个可怕的想法。 将引用与公开可用的对象相同的对象。如果任何其他写得不好的代码也决定锁定这个对象,那么两个完全不相关的事务将不得不等待彼此。isOnBoolean.FALSE

锁定是在对象实例上执行的,而不是对引用它们的变量执行的:

enter image description here