是否允许在 new Boolean(true) 上同步
Is it allowed to synchronize on new Boolean(true)
我很清楚在布尔值上同步不是好的做法。
有很多解释为什么它不好,例如:
Why is it not a good practice to synchronize on Boolean?
等等
所以,很明显这是错误的做法:
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.TRUE
和 Boolean.FALSE
是全局对象,如果它们在其他地方以类似的方式使用,这将产生一些难以检测的同步问题(感谢 Mark Rotteveel 指出它出)。
您正在同步对象引用。创建 private final Object lock = new Object();
与 private final static Boolean lock = new Boolean(true);
具有相同的效果。在这种情况下,您想要的只是一个独特的对象。
不过你应该小心,因为 new Boolean(true)
会创建一个 new 对象引用。如果您尝试使用 Boolean.TRUE
或 true
它们本质上是 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
上的同步将与 bool2
和 bool3
互斥,但 2 和 3 将共享排他性。
注意 identityHashCode
会给你参考哈希码,告诉你哪些对象 ==
相等。
由于 new Boolean(...)
为您提供了全新的 Object
,因此 lock
使用 new Boolean(...)
和 new Object()
的唯一区别在于Boolean
具有与之关联的 true
或 false
值,而 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.TRUE
和Boolean.FALSE
不一样。
注意:虽然这种做法看起来完全无害,但我认为尝试在 Boolean
、Integer
、String
或任何其他不可变对象,因为与 Object lock = new Object()
上的同步相比,它没有给您带来任何优势,同时又不是一个可识别的习语。这反映了你的代码的可读性,因为阅读你的代码的程序员会对你锁定 new Boolean(...)
.
的决定摸不着头脑。
从new Boolean(true) != new Boolean(true)
开始,你可以使用这样的锁。
不过要小心,因为 Boolean.valueOf(true) == Boolean.valueOf(true)
。
总而言之,如果您要维护该代码,只需按照您的建议将锁替换为 lock = new Object()
。
我很清楚在布尔值上同步不是好的做法。 有很多解释为什么它不好,例如:
Why is it not a good practice to synchronize on Boolean?
等等
所以,很明显这是错误的做法:
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.TRUE
和 Boolean.FALSE
是全局对象,如果它们在其他地方以类似的方式使用,这将产生一些难以检测的同步问题(感谢 Mark Rotteveel 指出它出)。
您正在同步对象引用。创建 private final Object lock = new Object();
与 private final static Boolean lock = new Boolean(true);
具有相同的效果。在这种情况下,您想要的只是一个独特的对象。
不过你应该小心,因为 new Boolean(true)
会创建一个 new 对象引用。如果您尝试使用 Boolean.TRUE
或 true
它们本质上是 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
上的同步将与 bool2
和 bool3
互斥,但 2 和 3 将共享排他性。
注意 identityHashCode
会给你参考哈希码,告诉你哪些对象 ==
相等。
由于 new Boolean(...)
为您提供了全新的 Object
,因此 lock
使用 new Boolean(...)
和 new Object()
的唯一区别在于Boolean
具有与之关联的 true
或 false
值,而 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.TRUE
和Boolean.FALSE
不一样。
注意:虽然这种做法看起来完全无害,但我认为尝试在 Boolean
、Integer
、String
或任何其他不可变对象,因为与 Object lock = new Object()
上的同步相比,它没有给您带来任何优势,同时又不是一个可识别的习语。这反映了你的代码的可读性,因为阅读你的代码的程序员会对你锁定 new Boolean(...)
.
从new Boolean(true) != new Boolean(true)
开始,你可以使用这样的锁。
不过要小心,因为 Boolean.valueOf(true) == Boolean.valueOf(true)
。
总而言之,如果您要维护该代码,只需按照您的建议将锁替换为 lock = new Object()
。