是否允许在 new Boolean(true) 上同步

Is it allowed to synchronize on new Boolean(true)

我很清楚在布尔值上同步不是好的做法。 有很多解释为什么它不好,例如:

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

http://telliott.io/node/40/

https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused

等等

所以,很明显这是错误的做法:

The code synchronizes on a boxed primitive constant, such as an Boolean.

private static Boolean inited = Boolean.FALSE; 
...
    synchronized(inited) 
    { 
        if (!inited) 
        { 
            init(); 
            inited = Boolean.TRUE; 
        } 
    } 
... 

我感兴趣的是如果我们用 "new" 运算符创建最终的静态布尔值会发生什么,即像这样的东西(这是我没有写过的真实代码但我维护它,方法名称等已更改):

private final static Boolean lock = new Boolean(true);
...
  public static SomethingGeneric getSomething()
  {
    synchronized(lock)
    {
      if (somethingElse == null)
      {
        try
        {
          somethingElse = persistence.valueobject.getSomeValue(GET_THAT);
          System.out.println("blah blah");
        }
        catch (ObjectCreationException oce)
        {
          // report the error
          log.error("There was this and that error", oce);
          System.out.println("Could not create it");
        }
      }
      return somethingElse;
    }
  }

那会不会"legal"使用它? 就好像我们使用了Object such in:

一样
private final Object lock = new Object();

private static final Object lock = new Object();

public void doSomething() {
  synchronized (lock) {
    // ...
  }
}

很好。你的第一个片段有两个问题,不是它是 Boolean 但首先,对象在这一行发生了变化:

 inited = Boolean.TRUE;

所以一些线程可以在旧对象上同步,一些在新对象上同步。这意味着,两组之间没有同步。

其次,Boolean.TRUEBoolean.FALSE 是全局对象,如果它们在其他地方以类似的方式使用,这将产生一些难以检测的同步问题(感谢 Mark Rotteveel 指出它出)。

您正在同步对象引用。创建 private final Object lock = new Object();private final static Boolean lock = new Boolean(true); 具有相同的效果。在这种情况下,您想要的只是一个独特的对象。

不过你应该小心,因为 new Boolean(true) 会创建一个 new 对象引用。如果您尝试使用 Boolean.TRUEtrue 它们本质上是 interned 并且将使用相同的实例,例如:

Boolean bool1 = new Boolean(true);
Boolean bool2 = Boolean.TRUE;
Boolean bool3 = true;

System.out.println(System.identityHashCode(bool1));
System.out.println(System.identityHashCode(bool2));
System.out.println(System.identityHashCode(bool3));

将打印

1433743869
19203296
19203296

因此 bool1 上的同步将与 bool2bool3 互斥,但 2 和 3 将共享排他性。

注意 identityHashCode 会给你参考哈希码,告诉你哪些对象 == 相等。

由于 new Boolean(...) 为您提供了全新的 Object,因此 lock 使用 new Boolean(...)new Object() 的唯一区别在于Boolean 具有与之关联的 truefalse 值,而 Object 则没有。

您可以通过 running 此代码段检查您是否通过调用 new Boolean(...) 获得了新对象:

Boolean bt = Boolean.TRUE;
Boolean bf = Boolean.FALSE;
for (int i = 0 ; i != 20 ; i++) {
    Boolean b = new Boolean(i % 2 == 0);
    System.out.println(b);
    System.out.println("==true : "+(b==bt));
    System.out.println("==false: "+(b==bf));
}

这会打印

true
==true : false
==false: false
false
==true : false
==false: false
true
==true : false
==false: false
false
==true : false
==false: false
...

意思是你得到的对象和Boolean.TRUEBoolean.FALSE不一样。

注意:虽然这种做法看起来完全无害,但我认为尝试在 BooleanIntegerString 或任何其他不可变对象,因为与 Object lock = new Object() 上的同步相比,它没有给您带来任何优势,同时又不是一个可识别的习语。这反映了你的代码的可读性,因为阅读你的代码的程序员会对你锁定 new Boolean(...).

的决定摸不着头脑。

new Boolean(true) != new Boolean(true)开始,你可以使用这样的锁。

不过要小心,因为 Boolean.valueOf(true) == Boolean.valueOf(true)

总而言之,如果您要维护该代码,只需按照您的建议将锁替换为 lock = new Object()