【发布时间】:2014-11-25 13:00:26
【问题描述】:
这个问题与 Initialization On Demand Holder 成语有关。我发现 here 它是线程安全的,没有 final 修饰符。
我希望这不是一个愚蠢的问题。
我有一个使用org.apache.commons.configuration.XMLConfiguration 的配置单例。好吧其实我有三个,以后可以增加。
它有一个默认的文件名来获取配置。
以后我需要能够定义一个新的配置文件名并使用它重新创建实例。
当我使用非线程安全的单例时,我曾经设置新的文件名,然后将包含实例的变量重置为 null,就是这样。下次使用该配置时,会自动使用新的配置文件名进行初始化。
我不能再使用“Initialization-on-demand holder idiom”那样做,因为我不能“消失”持有者类。 (对吧?)
我尝试重新分配包含该实例的静态变量,但显然它不起作用。
这是配置类的共同祖先:
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 中定义的,而不是在我的持有者“子”类中。
但我不想在每个配置单例中重复初始化。
希望它很清楚,有人可以阐明它。
谢谢大家。
编辑 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 wikipedia 网站所说的那样做。
【问题讨论】:
标签: java multithreading singleton