38

我的建筑师总是这么说

从不同步布尔值

我无法理解原因,如果有人可以举例说明为什么这不是一个好习惯,我将不胜感激。 参考示例代码

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

5 回答 5

72

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

你应该总是synchronize在一个常量对象实例上。如果您在您分配的任何对象上进行同步(即将对象更改为新对象),则它不是恒定的,不同的线程将在不同的对象实例上同步。因为它们在不同的对象实例上同步,所以多个线程将同时进入受保护的块,并且会发生竞争条件。Long这与在,Integer等上同步的答案相同。

// 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;

更糟糕的是,Boolean通过自动装箱 ( 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";
}

在您的情况下,因为看起来您需要使用线程打开/关闭它,所以您仍然需要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
    }
}

最后,如果您希望statusMessage从其他线程访问它,那么它应该被标记为volatile除非您synchronize在获取期间也会这样做。

于 2012-04-25T21:46:02.190 回答
21
private Boolean isOn = false;
public void doSomeStuffAndToggleTheThing(){
   synchronized(isOn){

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

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

在此处输入图像描述

于 2012-04-25T21:50:47.100 回答
1

我认为你的问题更多的是同步本身而不是布尔同步。想象一下,每个线程都是一条道路,语句(汽车)一个接一个地去。在某些时候可能会有一个交叉点:如果没有信号量,可能会发生冲突。Java 语言有一种内置的方式来描述这一点:因为任何对象都可以是交集,所以任何对象都有一个关联的监视器作为信号量。当您在代码中使用同步时,您正在创建一个信号量,因此您必须对所有道路(线程)使用相同的信号量。所以这个问题并不是真正的布尔特定的,因为只存在两个布尔值,每次你同步一个实例变量然后将同一个变量指向不同的对象时都会发生这个问题。所以你的代码对布尔值是错误的,但对整数同样危险,

于 2012-04-25T22:28:59.983 回答
1

所有包装类都是不可变的。人们不应该在它们上同步的主要原因之一。

就好像 2 个线程在包装类对象上同步,其中一个修改了它的值,它将在一个新的/修改的对象上同步,两个线程将在 2 个不同的对象上同步。所以,同步的整个目的就失去了。

于 2019-05-29T18:12:54.660 回答
-3

编辑:格雷的答案是正确的。

我要补充的是:您的架构师是对的,如果从角度来看Boolean不可变的,为什么要同步它?但是多线程很复杂并且基于场景。

于 2012-04-26T02:47:04.983 回答