用Singleton Pattern
实现的类如下,当多个线程访问此方法时,只有一个线程必须创建实例,所以我所做的只是同步该方法
private static synchronized FactoryAPI getIOInstance(){
if(factoryAPI == null){
FileUtils.initWrapperProp();
factoryAPI = new FactoryAPIImpl();
}
return factoryAPI;
}
我觉得这是不必要的,因为只有在第一次创建实例时才会创建,其余时间将返回已经创建的实例。将synchronised
添加到块时,一次只允许一个线程访问该方法。
getIOInstance
做两项工作
i( 初始化属性和
ii( 首次创建新实例
所以,我正在尝试在这里进行块级别synchronisation
,如下所示
private static FactoryAPI getIOInstance(){
if(factoryAPI == null){
synchronised {
if(factoryAPI == null){
FileUtils.initWrapperProp();
factoryAPI = new FactoryAPIImpl();
}
}
}
return factoryAPI;
}
我更喜欢第二个是正确的。我以正确的方式使用它吗?欢迎任何建议。
使用第一种方法,因为第二种方法不是线程安全的。
当你说,
factoryAPI = new FactoryAPIImpl();
编译器可以按以下顺序自由执行代码:
1( 在堆
上分配一些内存 2( 将工厂 API 初始化为该分配空间
的地址 3( 调用 FactoryAPI 的构造函数
问题是当另一个线程在步骤 2 之后和步骤 3 之前调用 getIOInstance(( 时。它可能会看到指向未初始化的工厂 API 实例的非空工厂 API 变量。
这个问题有许多不同的答案,例如,您可以在SEI上找到广泛的讨论。
现代Java解决方案很简单:使用枚举 - 因为JLS保证编译器/JVM将只创建一件事。
发现按需初始化持有者初始化方法,该方法初始化为一个有趣的方法,如下所示,
public class FactoryAPI {
private FactoryAPI() {}
private static class LazyHolder {
static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return FactoryAPI.INSTANCE;
}
}
由于 JLS 保证类初始化阶段是串行的,即非并发的,因此在加载和初始化期间不需要在静态 getInstance 方法中进行进一步的同步。
由于初始化阶段在串行操作中写入静态变量INSTANCE
,因此getInstance
的所有后续并发调用都将返回相同的正确初始化INSTANCE
,而不会产生任何额外的同步开销。