有相关的questions,但是作为重点,它们更多地是在volatile上,而不是在局部变量的使用上,因此,我要发布一个新问题,请参阅下面的问题。
class ResourceFactory {
private volatile Resource resource;
public Resource getResource() {
Resource localResource = resource;
if (localResource == null) {
synchronized (this) {
localResource = resource;
if (localResource == null) {
resource = localResource = new Resource();
}
}
}
return localResource;
}
static class Resource {
}
}
我了解我们有其他选择(比复杂的DCL更好),例如
我希望了解使用volatile和局部变量来解决DCL问题。
我也理解volatile(对于Java 1.5和更高版本)保证了“发生之前”功能,该功能可以防止返回不完整的对象以供读取。
问题1 我想理解的是,如果已经使用了局部变量,那么对volatile的需求是什么。下面的代码具有非易失性变量resource
,但是在getResource()
内使用局部变量读取resource
。代码中还有问题吗? (请在下面的代码中查看注释)
class ResourceFactory {
private Resource resource; // non volatile
public Resource getResource() {
Resource localResource = resource; // only 1 read without lock
if (localResource == null) {
synchronized (this) {
localResource = resource; // read inside the synchronized block
if (localResource == null) {
localResource = new Resource(); // constructor is initialized.
resource = localResource; // ****code reaches here only when constructor has been complete. Here is there a chance of resource being referring to incomplete object ?****
}
}
}
return localResource;
}
static class Resource {
}
}
我看到的唯一优势是volatile,它是假定对象已创建,然后第一次读取本身就可以保证内存读取,而不是从线程缓存读取,从而避免了不必要的锁获取。
问题2 如果使用简单的volatile而不是使用局部变量,那么代码没有问题吗?
答案 0 :(得分:2)
有效的Java 直接解决了问题2;虽然它也可能在其他版本中提到,但我面前的是第三版,第83条,p335:
代码可能看起来有些混乱。特别是,对局部变量(
result
)的需求可能不清楚。该变量的作用是确保field
在已经初始化的常见情况下仅被读取一次。尽管不是绝对必要的,但是这可以提高性能,并且通过应用于低级并发编程的标准可以更加优雅。在我的机器上,上述方法的速度是不带局部变量的明显方法的1.4倍。
(请注意,在早期印刷中,第三版Ed的代码示例中有一个严重错误,用于双重检查锁定(立即在此引号之前!请查看勘误以获取更正的代码。)