为什么在布尔值上同步不是一个好习惯? [英] Why is it not a good practice to synchronize on Boolean?
问题描述
我的建筑师总是说
从不同步布尔值
我无法理解原因,如果有人能举例说明为什么这不是一个好的做法,我将不胜感激.参考示例代码
I am not able to understand the reason why and would really appreciate if someone could explain with an example as to why it is not a good practice. 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"
您应该始终在常量对象实例上同步
.如果您在分配的任何对象上进行同步(即将对象更改为新对象),则它不是恒定的,并且不同的线程将在不同的对象实例上进行同步.因为它们在不同的对象实例上同步,所以多个线程将同时进入受保护的块并且会发生竞争条件.这是同步 Long
、Integer
等的相同答案.
You should always synchronize
on a constant object instance. If you synchronized on any object that you are assigning (i.e. changing the object to a new object) then it is not constant and different threads will be synchronizing on different object instances. Because they are synchronizing on different object instances, multiple threads will be entering the protected block at the same time and race conditions will happen. This is the same answer for synchronizing on Long
, Integer
, etc..
// 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
中所有对象的单例.你的锁对象应该是它所使用的类的本地对象,否则你会锁定同一个单例对象,而其他类如果犯了同样的错误,可能会在其他锁定情况下锁定.
To make matters worse, any Boolean
that is created through autoboxing (isOn = true
) is the same object as Boolean.TRUE
(or .FALSE
) which is a singleton in the ClassLoader
across all objects. Your lock object should be local to the class it is used in otherwise you will be locking on the same singleton object that other classes might be locking on in other lock cases if they are making the same mistake.
如果你需要锁定一个布尔值,正确的模式是定义一个 private final
锁定对象:
The proper pattern if you need to lock around a boolean is to define a private final
lock object:
private final Object lock = new Object();
...
synchronized (lock) {
...
或者你也应该考虑使用 AtomicBoolean
对象,这意味着你可能根本不需要同步
.
Or you should also consider using the AtomicBoolean
object which means you may not have to synchronize
on it at all.
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
并设置布尔值并避免测试/设置竞争条件:
In your case, since it looks like you need to toggle it on/off with threads then you will still need to synchronize
on the lock
object and set the boolean and avoid the test/set race condition:
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
也一样.
Lastly, if you expect the statusMessage
to be accessed from other threads then it should be marked as volatile
unless you will synchronize
during the get as well.
这篇关于为什么在布尔值上同步不是一个好习惯?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!