Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/324.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_Singleton - Fatal编程技术网

Java 单例使用;按需初始化持有者习惯用语;

Java 单例使用;按需初始化持有者习惯用语;,java,multithreading,singleton,Java,Multithreading,Singleton,这个问题与按需初始化持有者习惯用法有关。我发现它是线程安全的,没有最后的修改器 我希望这不是一个愚蠢的问题 我有一个使用org.apache.commons.configuration.XMLConfiguration的配置单例。事实上我有三个,以后可以增加 它具有从中获取配置的默认文件名 我需要能够在以后定义一个新的配置文件名,并使用它重新创建实例 当我使用非线程安全的单例时,我常常设置新的文件名,然后将包含实例的变量重置为null,就是这样。下次使用配置时,将使用新的配置文件名自动初始化该配

这个问题与按需初始化持有者习惯用法有关。我发现它是线程安全的,没有最后的修改器

我希望这不是一个愚蠢的问题

我有一个使用
org.apache.commons.configuration.XMLConfiguration
的配置单例。事实上我有三个,以后可以增加

它具有从中获取配置的默认文件名

我需要能够在以后定义一个新的配置文件名,并使用它重新创建实例

当我使用非线程安全的单例时,我常常设置新的文件名,然后将包含实例的变量重置为null,就是这样。下次使用配置时,将使用新的配置文件名自动初始化该配置

我不能再使用“按需初始化持有者习惯用法”这样做了,因为我不能“消失”持有者类。(对吗?)

我试图重新分配包含实例的静态变量,但显然它不起作用

这是配置类的共同祖先:

public class ConfiguracionBase {
    protected static String configFileName = "config.xml";

    protected static void abreConfiguracion(XMLConfiguration XMLConfig) throws ConfigurationException {
        //Should be placed in the same directory as this application.
        boolean exists = (new File(configFileName)).exists();
...
        if (exists) {
            try {
                XMLConfig = new XMLConfiguration(configFileName);
            } catch (ConfigurationException e) {
                if (e.getMessage().startsWith("Unable to load the configuration")) {
                    logger.fatal("IMPOSIBLE CONTINUAR: "+e.getMessage()+" ["+configFileName+"]",e);
                    StrUtil.writeToSimpleLog(Level.FATAL , "IMPOSIBLE CONTINUAR: "+e.getMessage()+" ["+configFileName+"]");
                    System.exit(Grales.EXIT_STATUS_ERROR_EN_CONFIGURACION);
                } else {
                    throw e; 
                }
            }

            XMLConfig.setThrowExceptionOnMissing(Boolean.TRUE);

...


    protected String getStringValue( [... some params ...] , XMLConfiguration XMLConfig) {

...

    protected int getIntValue( [... some params ...] , XMLConfiguration XMLConfig) {

...
public class Configuracion extends ConfiguracionBase {


    private static class ConfiguracionHolder {
        protected static Configuracion config = new Configuracion();
        protected static XMLConfiguration XMLConfig = null;

        private ConfiguracionHolder() throws ConfigurationException {
            abreConfiguracion(XMLConfig);
        }

    }

    private Configuracion() {
      // Exists only to defeat instantiation.
    }

    public static Configuracion getInstance() throws ConfigurationException {
        System.setProperty("user.timezone", ConfiguracionHolder.config.getTimezoneCFD());
        return ConfiguracionHolder.config;
    }

    public static void setConfigFileName(String configFileName) {
        ConfiguracionBase.configFileName = configFileName;
        ConfiguracionHolder.config = new Configuracion();
    }

...
这是我的一个配置类:

public class ConfiguracionBase {
    protected static String configFileName = "config.xml";

    protected static void abreConfiguracion(XMLConfiguration XMLConfig) throws ConfigurationException {
        //Should be placed in the same directory as this application.
        boolean exists = (new File(configFileName)).exists();
...
        if (exists) {
            try {
                XMLConfig = new XMLConfiguration(configFileName);
            } catch (ConfigurationException e) {
                if (e.getMessage().startsWith("Unable to load the configuration")) {
                    logger.fatal("IMPOSIBLE CONTINUAR: "+e.getMessage()+" ["+configFileName+"]",e);
                    StrUtil.writeToSimpleLog(Level.FATAL , "IMPOSIBLE CONTINUAR: "+e.getMessage()+" ["+configFileName+"]");
                    System.exit(Grales.EXIT_STATUS_ERROR_EN_CONFIGURACION);
                } else {
                    throw e; 
                }
            }

            XMLConfig.setThrowExceptionOnMissing(Boolean.TRUE);

...


    protected String getStringValue( [... some params ...] , XMLConfiguration XMLConfig) {

...

    protected int getIntValue( [... some params ...] , XMLConfiguration XMLConfig) {

...
public class Configuracion extends ConfiguracionBase {


    private static class ConfiguracionHolder {
        protected static Configuracion config = new Configuracion();
        protected static XMLConfiguration XMLConfig = null;

        private ConfiguracionHolder() throws ConfigurationException {
            abreConfiguracion(XMLConfig);
        }

    }

    private Configuracion() {
      // Exists only to defeat instantiation.
    }

    public static Configuracion getInstance() throws ConfigurationException {
        System.setProperty("user.timezone", ConfiguracionHolder.config.getTimezoneCFD());
        return ConfiguracionHolder.config;
    }

    public static void setConfigFileName(String configFileName) {
        ConfiguracionBase.configFileName = configFileName;
        ConfiguracionHolder.config = new Configuracion();
    }

...
My config.xml(默认配置)的货币格式如下:
$,0.00000000$(,0.00000000)

我的新configAbsoluteFileName的货币格式如下:
\,0.00000000;-\,0.00000000

启动应用程序时,我会执行以下操作:

    Configuracion.setConfigFileName(configAbsoluteFileName);
我添加了一个测试方法,它可以:

                    logger.debug("************ config file name: " + Configuracion.getConfigFileName() + " *******************");
                    logger.debug("************ money format: " + Configuracion.getInstance().getFormatoDineroHumanos() + " *******************");
即使文件名是新的,格式也是旧的

我认为这可能是因为配置文件名变量是在ConfiguracionBase中定义的,而不是在我的holder“sub”类中定义的

但我不想在每个配置单例中重复初始化

希望这是清楚的,有人能解释一下

谢谢大家


编辑1: 取代

    public static void setConfigFileName(String configFileName) {
        ConfiguracionBase.configFileName = configFileName;
        ConfiguracionHolder.config = new Configuracion();
    }
为了

但一切都没有改变


编辑2:

@fgb的回答真的很有帮助。多谢各位

根据维基百科网站,刚刚添加了一些更改

my ConfiguracionBase.abReconfiguration最终返回一个XMLConfiguration对象,并接收上一个对象,以便在未找到新文件名时保留它:

    protected static XMLConfiguration abreConfiguracion(XMLConfiguration XMLConfig, String configFileName) throws ConfigurationException {
...
        return XMLConfig;
    }
我的特定配置类有:

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
public class Configuracion extends ConfiguracionBase {

    private static volatile Configuracion config = null;
    private static XMLConfiguration XMLConfig = null;
    private static final Object objLock = new Object();

    private Configuracion() {
          // Exists only to defeat instantiation.
    }

    private Configuracion(String fileName) throws ConfigurationException {
        XMLConfig = abreConfiguracion(XMLConfig, fileName);
    }

    public static Configuracion getInstance() throws ConfigurationException {
        Configuracion result = config;
        if (result == null) {
            synchronized(objLock) {
                result = config;
                if (result == null) {
                    config = result = new Configuracion(configFileName);
                }
            }
        }
        System.setProperty("user.timezone", result.getTimezoneCFD());
        return result;

        /* http://en.wikipedia.org/wiki/Double-checked_locking: 
         * Note the local variable result, 
         * which seems unnecessary. This ensures that in cases where helper is already 
         * initialized (i.e., most of the time), the volatile field is only accessed once 
         * (due to "return result;" instead of "return helper;"), which can improve the 
         * method's overall performance by as much as 25 percent.[5] */
    }
...
现在一切正常

再说一遍:坦克


编辑3:


在getInstance()中添加了注释并更改了返回对象。在此之前,我犯了一个错误,所以我没有按照维基百科网站上说的去做

您没有创建
ConfiguracionHolder
的实例,因此不会调用其构造函数,也不会调用
abreconfiguration(XMLConfig)

我认为你没有从holder这个成语中得到任何好处。只有静态初始化是线程安全的,并且只运行一次。如果要更改
config
实例,则需要多次运行该配置,这在该习惯用法中是不可能的

如果有不同的线程调用
getInstance()
setConfigFileName()
那么它们之间需要某种同步。将
config
设置为volatile应该足够了

我将
abreconfiguration(XMLConfig)
移动到
Configuracion
的构造函数中,使
config
成为
Configuracion
的类变量,并使
configFileName
成为
Configuracion
构造函数的参数


另外,它看起来像是
abreconfiguration
应该返回
XMLConfig
,而不是将其作为参数。现在,它似乎没有设置任何在方法之外可见的内容。

两个问题:。1) setConfigFileName应仅将配置设置为null?。类似这样的内容
public static void setConfigFileName(String configFileName){ConfiguracionBase.configFileName=configFileName;config=null;}
或者应该创建一个新的配置。2) 它是如何与其他线程同步的?扩展问题1:我应该在
config=nullConfiguracion.getInstance()
?您需要文件名来创建
config
,因此我将在
setConfigFileName
方法中创建它,让
getInstance
只返回
config
setConfigFileName
总是会创建一个新实例,这样就不需要双重检查锁定,而且调用它的频率可能会降低,这样就不会成为瓶颈。