Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用单例方法以更好的方式实现同步?_Java_Multithreading_Synchronization_Thread Safety_Singleton - Fatal编程技术网

Java 如何使用单例方法以更好的方式实现同步?

Java 如何使用单例方法以更好的方式实现同步?,java,multithreading,synchronization,thread-safety,singleton,Java,Multithreading,Synchronization,Thread Safety,Singleton,使用单例模式实现的类如下所示,当多个线程访问此方法时,只有一个线程必须创建实例,所以我所做的就是同步该方法 private static synchronized FactoryAPI getIOInstance(){ if(factoryAPI == null){ FileUtils.initWrapperProp(); factoryAPI = new FactoryAPIImpl(); } return factoryAPI; }

使用
单例模式实现的类如下所示,当多个线程访问此方法时,只有一个线程必须创建实例,所以我所做的就是同步该方法

private static synchronized FactoryAPI getIOInstance(){
    if(factoryAPI == null){
        FileUtils.initWrapperProp();
        factoryAPI =  new FactoryAPIImpl();
    }
    return factoryAPI;
}
我觉得这是不必要的,因为只有在第一次创建实例时,才会返回已经创建的实例。将
synchronized
添加到块时,一次只允许一个线程访问该方法

getIOInstance
执行两项任务

i) 初始化属性和

ii)首次创建新实例

因此,我尝试在这里执行块级
同步
,如下所示

private static FactoryAPI getIOInstance(){
    if(factoryAPI == null){
        synchronised {
            if(factoryAPI == null){
                FileUtils.initWrapperProp();
                factoryAPI =  new FactoryAPIImpl();
             }
        }
    }
    return factoryAPI;
}

我宁愿第二个是正确的。我用得对吗?欢迎任何建议。

使用第一种方法,因为第二种方法不是线程安全的

当你说

factoryAPI = new FactoryAPIImpl();
编译器可以按照以下顺序自由执行代码:

1) 在堆上分配一些内存
2) 将factoryAPI初始化为该分配空间的地址
3) 调用FactoryAPIImpl的构造函数


问题是当另一个线程在步骤2之后和步骤3之前调用getIOInstance()时。它可能会看到指向未初始化factoryAPI实例的非空factoryAPI变量。

此问题有许多不同的答案,例如,您可以在上找到广泛的讨论

现代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
的所有后续并发调用都将返回正确初始化的
实例
,而不会产生任何额外的同步开销。

通过使字段
易变可以避免可见性问题。重复:在下面的链接中找到解决方案:第二个方法被调用,并且只有在遵循精确公式的情况下才能正确工作。把它当作一个更安全的替代品。听起来好像OP是在寻找一个懒散的加载单体。