java - 映射中的线程安全写入
问题描述
我在下面提供了一段代码:
Map<String, BigDecimal> salesMap = new HashMap<>();
orderItems.parallelStream().forEach(orderItem -> {
synchronized (this) {
int itemId = orderItem.getItemId();
Item item = settingsClient.getItemByItemId(itemId);
String revenueCenterName = itemIdAndRevenueCenterNameMap.get(itemId);
updateSalesMap(salesMap, "Gross Sales: " + revenueCenterName, orderItem.getNetSales().toPlainString());
}
});
private void updateSalesMap(Map<String,BigDecimal> salesMap, String key, String amount) {
BigDecimal bd = getSalesAmount(salesMap, key);
int scale = 2;
if (StringUtils.isBlank(amount)) {
amount = "0.00";
}
BigDecimal addMe = BigDecimal.valueOf(Double.valueOf(amount)).setScale(scale, RoundingMode.HALF_UP);
salesMap.put(key, bd.add(addMe));
}
该代码工作正常,但如果我不使用该synchronized
块,它将结束地图中的变化数据。据我所知,流是线程安全的,所以我很好奇发生了什么。我尝试使用 ConcurrentHashMap 但似乎没有任何改变。
我的想法是地图数据不写在 RAM 中,读/写是在线程缓存中完成的,因此我们最终会得到各种数据。
这是正确的吗?如果是这样,我将使用volatile
关键字,然后使用同步块。
注意:只是发现我不能volatile
在方法中声明变量。
解决方案
据我所知,流是线程安全的,所以我很好奇发生了什么。
他们是。只要您只对流本身进行操作。问题是您尝试同时操纵其他变量(在这种情况下为映射)。流的思想是对每个元素的操作是完全独立的——检查函数编程的思想。
我尝试使用
ConcurrentHashMap
,但似乎没有任何改变。
问题来自您的方法。一般的想法是原子操作ConcurrentHashMap
是线程安全的。但是,如果同时执行两个线程安全操作,它就不是原子和线程安全的。您需要自己同步它或提出其他解决方案。
在updateSalesMap()
方法中,您首先从地图中获取值,进行一些计算,然后更新值。这一系列操作不是原子的——执行它们ConcurrentHashMap
不会有太大变化。
在这种情况下实现并发的一种可能方法是利用CuncurrentHashMap.compute()
Javadocs
推荐阅读
- python - numpy 数组上的余数函数 (%) 运行时间远长于手动余数计算
- azure - 从 Microsoft azure 恢复已删除的 Web 应用
- c# - 双工 WCF 捕获客户端断开连接事件
- c - 如何用c打印文本模式?
- javascript - 如何使用 Django 将我的数独生成器结果呈现为 html 表?
- c# - How to set the Path of the Database for a Stimulsoft Report from the Code in C#?
- typescript - ts Cannot find name ... with typeRoots specified explicity
- javascript - How to use Jquery validation on the radio button with respected fields?
- apache - prestashop URL 无意义重定向
- php - OneSignal 不想用新通知替换旧通知