此问题与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维基百科网站所说的话。
答案 0 :(得分:0)
您不创建ConfiguracionHolder
的实例,因此不会调用其构造函数,并且不会调用abreConfiguracion(XMLConfig)
。
我不认为你从持有人的习语中获得任何好处。它只是静态初始化,它是线程安全的,并且只运行一次。如果要更改config
实例,则需要多次运行配置,这是该惯用法无法实现的。
如果您有不同的线程呼叫getInstance()
和setConfigFileName()
,那么您需要在它们之间进行某种同步。使config
不稳定就足够了。
我会将abreConfiguracion(XMLConfig)
移到Configuracion
的构造函数中,使config
成为Configuracion
的类变量,并使configFileName
成为Configuracion
的参数1}}构造函数。
此外,看起来abreConfiguracion
应该返回XMLConfig
而不是将其作为参数。现在,它似乎没有设置在方法之外可见的任何东西。