以下代码有什么问题?。
private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
Record rec = records.get(id);
if (rec == null) {
rec = new Record(id);
records.put(id, rec);
}
return rec;
putIfAbsent
?答案 0 :(得分:6)
这不是线程安全的。
如果还有另一个帖子,那么在records.get
和records.put
之间的时间内,另一个帖子也可能放置了该记录。
只读操作(即不修改结构的操作)可以由多个线程同时完成。例如,1000个线程可以安全地读取int
的值。但是,如果没有某种锁定操作,那1000个线程无法更新int
的值。
我知道这可能听起来像是一个非常不可能发生的事件,但请记住,百万分之一的事件在1GHz时每秒发生1000次。
这是线程安全的:
private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
// presumably aMap is a member and the code below is in a function
aMap.putIfAbsent(id, new Record(id))
Record rec = records.get(id);
return rec;
请注意,这可能会创建Record
并且永远不会使用它。
答案 1 :(得分:3)
它可能是也可能不是线程安全的,具体取决于您希望它如何行动。
在代码结束时,aMap
将Record
安全地id
。但是,两个线程可能都创建并放入Record
,这样就存在两个(或更多,如果有更多线程的话)Records
。这可能没问题,也可能不是 - 真的取决于你的申请。
线程安全的危险之一(例如,如果你使用普通的HashMap
没有同步)是线程可以跨线程读取部分创建或部分更新的对象;换句话说,事情可能真的乱七八糟。这将 not 在您的代码中发生,因为ConcurrentHashMap
将确保线程之间的内存保持最新,并且在这种意义上它是线程安全的。
你可以做的一件事就是使用putIfAbsent
,它会自动将一个键值对放入地图中,但前提是该键上没有任何内容:
if (rec == null) {
records.putIfAbsent(id, new Record(id));
rec = records.get(id);
}
在这种方法中,您可能会创建第二个Record
对象,但如果是这样,它将不会被插入并立即可用于垃圾回收。截至摘录的末尾:
records
将包含Record
的给定ID Record
会被放入records
以获取该ID(无论是由此线程还是其他线程放入)rec
将指向该记录