我有许多带有他自己的hashmaps的服务,我的hashmaps是不可变的,它们在初始化程序块中初始化。因此,所有线程都将读取哈希Map,但从不在其上写入。
我的服务是spring的组件,所以它们是“spring单例”。
使用hashmap做这个有什么问题吗?我应该用别的课吗?为什么?
下面是我所说的一个例子:
@Service
public class TrackingServiceOne extends TrackingService {
Map<Integer, String> mapCreditos = new HashMap<Integer, String>();
Map<Integer, String> mapDeudas = new HashMap<Integer, String>();
Map<Integer, String> mapEjemplo = new HashMap<Integer, String>();
{
mapCreditos.put(1, "hi");
mapCreditos.put(2, "example");
// And like this I will populate each map.
}
// My methods will read the maps, never write them.
}
4条答案
按热度按时间ymzxtsji1#
hashmap对于只读并发访问是安全的。确保hashmaps在线程启动之前被初始化,并且只要没有其他线程写入它们,就可以在没有任何争用条件的情况下从多个线程使用它们。
dwbf0jvd2#
原则上,使用
HashMap
在一个并行的方式,只要你真的确保没有修改!随着代码的发展,您应该考虑使用
Collections.unmodifiableMap()
以确保以后没有人可以修改。为此,构造函数初始化可能是最简单的方法。还要确保既不暴露贴图本身,也不暴露任何类似
keySet()
或者values()
到教室外面去。ryoqjall3#
Map.copyof
正如其他答案所说,任何
Map
对于只读访问,应该是线程安全的。要确保Map仅用于读取而不用于写入,请使用不能修改的Map。制作不可修改Map的现代方法是使用
Map.copyOf
在Java10及更高版本中。填充您的
HashMap
使用临时对象。然后制作一个不可修改的副本作为对象上的成员字段。anauzrmj4#
是的,正式地说,当您在一个线程中读取hashmap而不是用于编写时,可能会出现问题。见下文。。。
有一种流行的二分法:数据竞争和竞争条件(例如,参见https://dzone.com/articles/race-condition-vs-data-race). 自
HashMap#get()
不修改hashmap的状态,正如其他答案所说的,在同步读取上没有竞争条件。但是,你也必须避免数据竞争。显然,你必须保证在你的读者开始阅读之前,所有的写作都完成了。这意味着您需要在写入和读取之间实现线性化。在实践中/就java代码而言,这将导致一种hb(https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5)在写入和读取之前。举几个例子:
完全填写Map后,将对Map的引用保存在最后一个字段中(https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5)
Thread#start
生成hb edge,因此,如果您自己管理线程,请将填充的Map传递给读取器线程,并且仅在调用它们的start()
. 您应该从writer线程执行pass和start操作读取线程正在等待
CountDownLatch
编写线程调用countDown()
填完Map后。读者开始使用Map。也可以使用forkjoinpool/executorservice,因为if在引擎盖下生成所需的hbs。执行/安排一个任务来填充Map,只有在任务完成后,才执行/安排所有读卡器。
等等。。。
因此,如果您真的保证(从jmm的Angular )在开始任何读取之前完成最后一次写入,那么恭喜您,您也没有数据竞争。
第一个例子好像是你的案子。。。即使Spring在引擎盖下施了一些无形的魔法,使你的Map正确可见(没人能证明,是吗?:),就jmm而言,我更喜欢以下规范和明确的安全出版物:
以下不太显式的形式也可以,因为如果类中至少定义了一个final字段,则其构造函数将以冻结操作结束:
观察https://shipilev.net/blog/2014/all-fields-are-final/ 一些有趣的热点相关细节