我使用单例来读取配置:
Class Property {
private String username;
private String password;
private static Property props;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public static Property getInstance() {
if(props == null) {
synchronized(Property.class) {
props = new Property();
props.initial();
}
}
return props;
}
public void initial() {
Properties prop = new Properties();
prop.load(new FileInputStream(new File("application.properties")));
username = prop.getProperty("username");
password = prop.getProperty("password");
}
}
然后,在第一个帖子中,我得到了一个Property实例,例如props = Property.getInstance
我调用了方法getUsername()
和getPassword()
,如下所示:
props.getUsername()
/ props.getPassword()
。
但是,这两种方法返回null 。在第二个线程和后面的线程中,我可以从这两个方法中获取用户名和密码。
我不知道为什么会这样。有人可以帮我这个吗?
答案 0 :(得分:4)
在props
方法
prop
更改为initial()
public void initial() {
Properties prop = new Properties();
prop.load(new FileInputStream(new File("application.properties")));
username = prop.getProperty("username");
password = prop.getProperty("password");
}
答案 1 :(得分:1)
"道具"执行新Property()
后不再为null。因此,即使props
- 方法未完全执行,也可能会返回initial()
。
请尝试以下代码:
public static Property getInstance() {
synchronized(Property.class) {
if(props == null) {
props = new Property();
props.initial();
}
}
return props;
}
答案 2 :(得分:0)
我认为上面代码中的props.getProperty在发布问题时是一个错字。
在synchronized语句之前,检查props == null。可能已创建道具(新属性),因此不为空,但对初始的调用尚未完成。因此,在调用getUsername / password之前,getInstance返回的对象尚未正确初始化。最简单的解决方案是使整个方法同步,但这可能是性能问题。
此外,使用当前实现,您最终可能会创建单例的两个实例。例如。如果props == null并且最终两个线程都进入synchronized块。你应该检查同步部分中的props是否为null,以避免创建两个实例(参见:http://en.wikipedia.org/wiki/Double-checked_locking)
处理多线程单例的更优雅方式可能是按需初始化习语(http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom)。
答案 3 :(得分:0)
antonis_wrx和Andy N.所说的是你的同步不是"宽"够:
1: if(props == null) {
2: synchronized(Property.class) {
3: props = new Property();
4: props.initial();
5: }
6: }
考虑并行执行2个线程。线程A执行第1行,props == null
返回true。在此之后,OS切换到线程B.线程B也执行相同的第1行,并且检查仍然返回true(props仍然为null)。现在两个线程将继续并执行synchronized块。
所以说线程A首先在Property.class上获取监视器(第2行)并初始化道具(第2-5行)。现在线程B可以自由地获取第2行中的监视器 - 它执行第3行,此时,线程C调用getInstance().getUsername()
。
线程C getInstance
执行返回false的第1行,然后返回props
(它刚刚由线程B构建到new Property()
)。在这个未初始化的Property对象上调用getUsername()
会给出您看到的null。
请注意,对于此示例,Thread C可能也是相同的Thread A(我只是想让它更容易阅读)。
您可以使用已建议的内容解决问题(同步整个getInstance
方法)。另一种方法是使用静态块为您执行初始化,如下所示:
Class Property {
private String username;
private String password;
private static Property props;
static {
props = new Property();
props.initial();
}
...
}