我的建筑师总是这么说
Never synchronize on Boolean
我无法理解原因,如果有人能用例子解释为什么这不是一个好的做法,我将非常感激。 Reference Sample Code
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
}
}
}
最佳答案
I am not able to understand the reason why we should "never synchronize on Boolean"
您应该始终在常量对象实例上同步
。如果您对正在分配的任何对象进行同步(即将对象更改为新对象),则它不是恒定的,并且不同的线程将在不同的对象实例上进行同步。因为它们在不同的对象实例上同步,所以多个线程将同时进入 protected block ,并且会发生竞争条件。这与 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
),它是ClassLoader
中跨所有对象的单例。您的锁定对象应该是它所使用的类的本地对象,否则您将锁定同一个单例对象,而其他类在其他锁定情况下可能会锁定相同的单例对象(如果它们犯了同样的错误)。
如果您需要锁定 boolean 值,正确的模式是定义一个私有(private)最终
锁定对象:
private final Object lock = new Object();
...
synchronized (lock) {
...
或者您还应该考虑使用 AtomicBoolean
对象,这意味着您可能根本不需要同步
它。
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";
}
就您而言,由于您似乎需要使用线程打开/关闭它,因此您仍然需要在lock
对象上进行synchronize
并设置 boolean 值并避免测试/设置竞争条件:
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
,除非您在执行期间同步
也得到。
关于java - 为什么在 boolean 值上同步不是一个好的做法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56428653/