Java 我的代码已经是线程安全的了吗
我有一个单身班:Java 我的代码已经是线程安全的了吗,java,multithreading,Java,Multithreading,我有一个单身班: public class MySingleton { private static MySingleton instance; // here the Map entry's value is also a Map private Map<String, Map> dataMap; private MySingleton() { dataMap = new HashMap<String, Map>(); Map&
public class MySingleton {
private static MySingleton instance;
// here the Map entry's value is also a Map
private Map<String, Map> dataMap;
private MySingleton() {
dataMap = new HashMap<String, Map>();
Map<String, String> dataTypeOneMap = new HashMap<String, String>();
Map<String, String> dataTypeTwoMap = new HashMap<String, String>();
dataMap.put("dataTypeOne", dataTypeOneMap);
dataMap.put("dataTypeTwo", dataTypeTwoMap);
}
public static MySingleton getInstance() {
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
public synchronized void storeData(String data, String type) {
dataMap.get(type).put("my_data", data);
}
public synchronized String getData(type) {
return dataMap.get(type).get("my_data");
}
}
对于
dataMap
,是否需要使用ConcurrentHashMap
类型?或者它已经是线程安全的了?我觉得我的代码已经是线程安全的了,但我只是想确保没有任何极端情况会破坏它。谢谢。简短回答:
这部分代码不是线程安全的:
public static MySingleton getInstance() {
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
长答案
让我们逐一处理代码部分
例如,假设一个线程正在执行getInstance方法,并且达到了if条件:
if(instance == null) {
现在,如果另一个线程开始调用该方法,if条件仍然为true,它还将尝试创建一个新实例。因此,它可能会创建一些用例:
synchronized(MySingleton.class){
if(instance == null) {
instance = new MySingleton();
}
}
不,它不是线程安全的。使用允许从不同线程进行访问和修改。它还允许您异步读取映射,而不会影响性能
同步getInstance()方法,并尽量减少对该方法的调用量-在另一个线程上运行的每个对象上存储对单例的引用以避免重复getInstance()调用将有所帮助。根据注释,实例的延迟实例化不是线程安全的,即如果两个线程调用
getInstance()
同时进行两次调用可能会导致instance=new MySingleton()
甚至返回一个不同的实例
,这意味着两个线程操作不同的对象
要解决这个问题,您可以在类本身上同步getInstance()
,甚至使用双重检查锁定来减少同步开销(如果您不能首先消除懒惰):
使用双重检查锁定,您将只能同步创建部分,检查null并返回实例(如果不是null),而不必是线程安全的
dataMap
是线程安全的。但是您可以进一步优化代码。您可以用ConcurrentHashMap
替换Map
,并在storeData
和getData
方法中删除synchronized
您可以查看此文档页面,了解它是否真的必须懒惰?那部分不是线程安全的。@Fildor,你能分享更多你的想法吗?谢谢。除了问题:请注意,如果
type
的值不在映射中,您会在storeData中得到一个NPE。请考虑两个线程第一次调用getInstance()
:两个线程都可能得到不同的实例,因为在调用instance
时为空,如果一个线程调用storeData(),您将丢失数据
在一个被另一个覆盖的实例上。还有其他详细信息吗?它们是如何修复的?这就是您回答问题的方式。请您解释一下“它不是线程安全的”,谢谢。请注意,此
在静态方法中不可用。“你需要另一个锁监视器,例如班级本身”。@Thomas,好帮手。错过了。使用适当的解决方案更新答案。使用ConcurrentHashMap
无法解决getInstance()
不具备线程安全性的问题,但它是一个单一的、已使用的公共方法synchronized
关键字,为什么它不具备线程安全性?你能再解释一下吗?谢谢。因为私有静态MySingleton实例可以通过多次调用getInstance()
多次重新分配code>,因为此方法不可用synchronized@Brad仅当他没有在每个数据库上存储对象的实例时thread@Brad如何重新分配MySingleton?单例的目的不是为了防止这种事情发生吗?在Java 8中,实例
是否仍然必须是易变的?复杂的东西:)@Fildor啊,我不完全确定,但根据“读取和写入对于引用变量是原子的”所以我说不。-更新:根据Jack volatile链接的wiki文章,volatile可能仍然有助于防止由于编译器重新排序而导致的错误。添加volatile
无论如何都不会有什么坏处。@Thomas,因此,作为一个结论,dataMap
不需要使用ConcurrentHashMap,对吗?只有延迟初始化才是需要解决的问题?对于实例
变量,使用volatile
很好,我还好吗?@Leem.fin看看Jack链接的wiki文章。当然,我会包括volatile
。至于映射:如果同步所有访问映射的方法,但删除该同步并使用ConcurrentHashMap
可以提高性能,那么就不需要ConcurrentHashMap
,因为它使用(大部分)无锁算法(即,不同存储桶上的写入/读取通常根本不需要同步)。
synchronized(MySingleton.class){
if(instance == null) {
instance = new MySingleton();
}
}
private volatile MySingleton instance;
public static MySingleton getInstance() {
//check if the instance doesn't exist yet, this is not threadsafe
if(instance == null) {
//the instance doesn't exist yet so synchronize and check again, since another thread
// might have entered this block in the meantime and instance is not null anymore
synchronized(MySingleton.class) {
if(instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}