Singleton使用"初始化按需持有者成语"

时间:2014-09-30 19:31:44

标签: java multithreading singleton

此问题与Initialization On Demand Holder成语有关。我发现here它是线程安全的,没有最终修饰符。

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

我有一个使用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();
    }

...

我的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中定义的,而不是在我的持有者" sub"类。

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

希望它清楚,有人可以对此有所了解。

谢谢大家。


编辑1: 取代

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

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

但没有改变


编辑2:

@fgb回答真的有帮助。非常感谢你。

根据Double-checked_locking维基百科网站添加了一些更改。

我的ConfiguracionBase.abreConfiguracion最终返回一个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()中的返回对象。在此之前我犯了一个错误,所以我没有做Double-checked_locking维基百科网站所说的话。

1 个答案:

答案 0 :(得分:0)

您不创建ConfiguracionHolder的实例,因此不会调用其构造函数,并且不会调用abreConfiguracion(XMLConfig)

我不认为你从持有人的习语中获得任何好处。它只是静态初始化,它是线程安全的,并且只运行一次。如果要更改config实例,则需要多次运行配置,这是该惯用法无法实现的。

如果您有不同的线程呼叫getInstance()setConfigFileName(),那么您需要在它们之间进行某种同步。使config不稳定就足够了。

我会将abreConfiguracion(XMLConfig)移到Configuracion的构造函数中,使config成为Configuracion的类变量,并使configFileName成为Configuracion的参数1}}构造函数。

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