Why is it not a good practice to synchronize on Boolean?

大憨熊 提交于 2019-11-26 03:34:34

问题


My architect always says that

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

回答1:


I am not able to understand the reason why we should "never synchronize on Boolean"

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

To make matters worse (as @McDowell pointed out) 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.

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) {
   ...

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

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

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.




回答2:


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

This is a terrible idea. isOn will reference the same object as Boolean.FALSE which is publicly available. If any other piece of badly written code also decides to lock on this object, two completely unrelated transactions will have to wait on each other.

Locks are performed on object instances, not on the variables that reference them:




回答3:


I think your problem is more with synchronized itself than with sync'ing on Booleans. Imagine that each Thread is a road, where statements (cars) go one after another. At some point there may be an intersection: without a semaphore collisions may happen. The Java language has a built in way to describe this: since any object can be an intersection, any object has an associated monitor acting as a semaphore. When you use synchronized in your code, you are creating a semaphore, thus you must use the same one for all the roads (threads). So this problem is not really boolean-specific because only two Booleans exist, this problem happens every time you synchronize on an instance variable and then point the same variable to a different object. So your code is wrong with Booleans, but is equally dangerous with Integers, Strings and any Object if you don't understand what's going on.




回答4:


All wrapper classes are immutable. One of the major reason why one should not synchronize on them.

As if 2 threads synchronize on wrapper class object and one of them modifies its value,it will be synchronized on a new/modified object and both threads will be synchronized on 2 different objects. So,The whole purpose of synchronization is lost.




回答5:


Edit: Gray's answer is correct.

What I want to add is: Your architect is right, if from perspective of Boolean is immutable, why synchronize it? But multi thread is complex and based on the scenario.



来源:https://stackoverflow.com/questions/10324272/why-is-it-not-a-good-practice-to-synchronize-on-boolean

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!