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=null类似这样的代码:Configuracion.getInstance()
?您需要文件名来创建config
,因此我将在setConfigFileName
方法中创建它,让getInstance
只返回config
setConfigFileName
总是会创建一个新实例,这样就不需要双重检查锁定,而且调用它的频率可能会降低,这样就不会成为瓶颈。