为什么 Netbeans 抱怨用条件包装同步调用?
假设我有一些像这样的代码:
private static Thing t = null;
private static final Object lock = new Object();
public static void foo() {
if(t == null) { //Netbeans warning on this line.
synchronized(lock) {
if(t == null) {
t = new Thing();
}
}
}
...Do stuff with t...
}
现在, foo 托管在服务器上,并且可能被许多用户同时调用。函数 foo 的目的是初始化 t(如果尚未初始化),否则使用初始化的实例来执行任何需要执行的操作。
然而,Netbeans 给我这样的代码警告:
删除外部条件语句
带有工具提示:
双重检查锁定
双重检查锁定的要点是避免瓶颈等待人们释放锁。如果唯一检查 t 是否为 null 的检查是在同步块内部,那么每个人都必须等待有机会查看 t 是否为 null,而 everybody-1 实际上什么也不等待(因为 t 不会为 null) 。因此是外部条件。
有了它,比如说,奇迹般地,5 个人同时将 t == null 解析为 true。一个用户将获得同步锁,看到 t == null 仍然为 true,并初始化它。然后,该用户将释放锁,下一个用户将锁定它,并几乎立即释放它(因为 t != null 不再),这将继续,直到 5 个用户通过它。一直以来,其他连接的用户都会跳过同步锁,因为第一个用户使 t != null。
那么,Netbeans 为何抱怨呢?据我所知,这似乎是最好的方法。
想法?
Say I have some code like so:
private static Thing t = null;
private static final Object lock = new Object();
public static void foo() {
if(t == null) { //Netbeans warning on this line.
synchronized(lock) {
if(t == null) {
t = new Thing();
}
}
}
...Do stuff with t...
}
Now, foo is hosted on a server, and could possibly be called by many users at the same time. The purpose of the function foo is to initialize t if it hasn't been already, otherwise, use the initialized instance to do whatever stuff needs to be done.
However, Netbeans gives me a warning on code like this:
Remove the outer conditional statement
With a tooltip:
Double-checked locking
The point of the double checked locking is to avoid a bottleneck waiting for people to release the lock. If the only check to see if t was null was inside of the synchronized block, everybody would have to wait for their chance to see if t was null, and everybody-1 would effectively wait for nothing (because t wouldn't be null). Hence the outer conditional.
With it, say, by some miracle, 5 people resolve t == null to true at the same time. One user will get the synchronized lock, see that t == null is still true, and initialize it. Then, that user will release the lock, and the next user will lock on it, and almost immediately release it (as t != null anymore), this will continue until the 5 users have passed through it. All the while, other users connecting jump over the synchronized lock, as the first user in made t != null.
So, why is Netbeans complaining? This seems like the best way to do it AFAIK.
Thoughts?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
它抱怨是因为按照书面规定,它是不安全的——或者至少可能是。
由于
t
不是易失性
,因此读取器线程可能会在时读取
仍在构建中。这将是一个糟糕的举动——使用部分构造的对象是可怕的。t
的新值。事物在 Java 5 的新内存模型之前,如果字段标记为
易失性
,它甚至不是线程安全的。使用新的内存模型在某些情况下是安全的,甚至可能没有易失性
,但我个人无论如何都会避免它:通常作为静态初始化程序的一部分进行初始化就足够了:
对于其他情况,还有嵌套类的替代技巧,它们仍然使用静态初始化器的惰性,但所有这些单个属性都是惰性的即使在访问其他成员时。
有关 DCL 旧问题的更多信息,请阅读 “双重检查锁定已损坏”声明。 (请参阅最后部分了解 JDK 5 的当前情况 - 但请考虑一下您是否真的希望代码的维护者考虑所有这些。)
It's complaining because as written, it's unsafe - or at least may be.
Because
t
isn'tvolatile
, it may be possible for a reader thread to read the new value oft
whileThing
is still being constructed. That would be a bad move - the use of partially-constructed objects is hideous.Before Java 5's new memory model, it wouldn't even have been thread-safe with the field marked as
volatile
. With the new memory model that's safe in certain situations, potentially even withoutvolatile
, but personally I'd avoid it anyway:Usually initializing as part of the static initializer is good enough:
For other cases there are alternative tricks with nested classes which still use the laziness of static initializers, but all this single property to be lazy even when the other members are accessed.
For a lot more information on the old brokenness of DCL, read The "Double-Checked Locking is Broken" Declaration. (See the end section for the current situation as of JDK 5 - but think about whether you really want maintainers of your code to be thinking about all of this.)