Java 为什么在Boolean上同步不是一个好的做法?

Java 为什么在Boolean上同步不是一个好的做法?,java,multithreading,boolean,synchronize,Java,Multithreading,Boolean,Synchronize,我的建筑师总是这么说 从不在布尔值上同步 我无法理解为什么,如果有人能举例说明为什么这不是一个好的做法,我将不胜感激。 我无法理解为什么我们“永远不要在布尔值上同步” 您应该始终对常量对象实例进行同步。如果在指定的任何对象上同步(即将对象更改为新对象),则该对象不是常量,不同的线程将在不同的对象实例上同步。因为它们在不同的对象实例上同步,所以多个线程将同时进入受保护的块,并且会发生争用情况。这与在Long、Integer等上进行同步的答案相同 // this is not final so i

我的建筑师总是这么说

从不在布尔值上同步

我无法理解为什么,如果有人能举例说明为什么这不是一个好的做法,我将不胜感激。

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

您应该始终对常量对象实例进行
同步
。如果在指定的任何对象上同步(即将对象更改为新对象),则该对象不是常量,不同的线程将在不同的对象实例上同步。因为它们在不同的对象实例上同步,所以多个线程将同时进入受保护的块,并且会发生争用情况。这与在
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;
更糟糕的是,通过自动装箱(
isOn=true
)创建的任何
Boolean
)都是与
Boolean.true
(或
.FALSE
)相同的对象,它是
类加载器中跨所有对象的单例。您的锁对象应该是它所使用的类的本地对象,否则您将锁定在同一个单例对象上,如果其他类犯了相同的错误,那么其他类可能会锁定在同一个单例对象上

如果需要锁定布尔值,正确的模式是定义
private final
lock对象:

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

synchronized (lock) {
   ...

或者您也应该考虑使用<代码> AtomicBoolean < /Cord>对象,这意味着您根本不必在代码> >同步/<代码>。

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";
}
在您的情况下,因为看起来您需要使用线程来打开/关闭它,所以您仍然需要在
锁定
对象上
同步
,并设置布尔值,避免测试/设置竞态条件:

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
,除非在get过程中也将
同步

private Boolean isOn = false;
public void doSomeStuffAndToggleTheThing(){
   synchronized(isOn){
这是个糟糕的主意
isOn
将引用与
Boolean.FALSE
相同的对象,该对象公开可用。如果其他任何一段写得不好的代码也决定锁定这个对象,那么两个完全不相关的事务将不得不互相等待

锁定是对引用它们的变量执行的,而不是对引用它们的变量执行的:


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

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

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

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


就像两个线程在包装类对象上同步,其中一个修改了它的值一样,它将在一个新的/修改过的对象上同步,两个线程将在两个不同的对象上同步。因此,同步的全部目的都失去了。

您的示例代码来自一篇解释为什么它不好的博客文章@詹姆斯蒙塔涅:是的,但我没有清楚地理解解释。@Rachel我当然能理解她或他!无论创建多少布尔变量,在运行的JVM中只有两个
java.lang.Boolean
对象实例。这会产生严重的别名:每个人都在同一对对象上同步,而他们的程序使其看起来像是在完全不同的实例上同步@您可以在需要互斥的线程之间有意共享的对象上进行同步。每个需要同步的逻辑区域使用一个同步对象。在程序的
synchronized
块中使用的变量可能有很多这样的区域(可能有很多)。假设有十个区域需要互斥,因此创建了十个同步对象。然而,当你的对象是布尔对象时,你所有的十个变量都只指向两个对象-
True
False
,因此你的程序将无法按你的预期工作。@dasblinkenlight这也是一个罕见的问题,实际上测试了受访者的一些有用知识。我见过一些很难找到的竞争条件,其中经验丰富的程序员违反了这一点背后的一般原则——尽管Java确实让这一点变得太容易了(在
上同步这个
——例如,如果您还扩展了线程而不是使用runnable,那么这是一个不错的错误)你能举一个其他会导致死锁的坏代码的例子吗?在布尔值上同步的任何其他代码,即使他们将其称为除
isOn
之外的其他代码。只有两个布尔对象,因此所有具有的线程都可能相互竞争。哎哟,这才是真正的问题。虽然我认为对为什么在你无法完全控制的对象上同步是个坏主意的更一般性的解释会更好——在单个对象上同步
private Boolean isOn = false;
public void doSomeStuffAndToggleTheThing(){
   synchronized(isOn){