关于多线程的,书中给了一个延迟初始化的例子,用的是双重检查方式:
// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized (this) {
if (field == null) // Second check (with locking)
field = result = computeFieldType();
}
}
return result;
}
这里是原书第三版的第 83 条:
这里面的局部变量 result 不是很好理解,而且我在 JDK 21 下跑出来是可能取到 null 的,求大佬解释一下~
破案了,就是错的,看这里https://github.com/jbloch/effective-java-3e-source-code/issues/8,issues 里面给出了解决方法:
private FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized (this) {
if (field == null) // Second check (with locking)
field = result = computeFieldValue();
else
result = field;
}
}
return result;
}
1
beneo 2023-12-28 17:07:39 +08:00
这个语法我都没见过了
field = result = computeFieldType(); |
2
cover 2023-12-28 17:17:54 +08:00
自问自答?
|
3
cover 2023-12-28 17:20:01 +08:00
就是两个线程一起 synchronized 锁的时候,field 初始化成功了,但是 result 没值,后进 synchronized 那根线程拿到了 null ,修复方案也显而易见,就是让后进的线程也拿到已经被前置线程赋值完成的 field ,其实我觉得最后返回 field 更加方便。
|
4
Bronya OP @cover #2 刚开始没找到那个 ussues ,发完帖子之后又搜索了一下,发现原来真的是作者搞错了,然后赶紧编辑了一下,看起来就像自问自答了😂
|
5
flython6 2023-12-28 17:27:19 +08:00
这个例子确实是错的返回 result
但是我纠结的是为什么要使用局部变量……解释感觉跟没说的一样 |
6
flython6 2023-12-28 17:32:16 +08:00 1
@cover 哦我懂作者意思了,确实 else 直接返回 field 也可以,还省了一步赋值操作
但是最外面返回 result 主要是想要避免 volatile 变量读取时的缓存行失效,这样可以提升性能(着实是没什么用处的提升) 如果 DCL 最外层检测失败,或者修改后代码没进入 else ,也可以确保最少限度地访问 field (因为每次访问 volitale 都会使缓存行失效从而从主内存加载最新变量副本到工作缓存) 我理解应该是这个意思,欢迎指正 |
7
kneo 364 天前 via Android
别用这种方法,古老而易错。在我看来这是一个 anti pattern ,应该从 effective java 里拿掉。
搜索一下 enum singleton 。或者,就用一个普通的锁。 |