【问题标题】:Safe access to multithreaded HashMap Java安全访问多线程 HashMap Java
【发布时间】:2014-05-25 09:07:45
【问题描述】:

我有一个应用程序,它有一个 ConcurrentHashMap,它在本地存储存储在外部服务器上的数据副本。地图每隔几秒就会使用新的数据副本进行更新。

我有一个每隔几秒钟运行一次的循环,它可以访问 HashMap 并按值的顺序将元素添加到数组中(它实际上做得更多,但这无关紧要)。我的问题是,如果数据在创建数组的过程中发生变化,您可能在不同的地方有重复的键或完全遗漏了一些键。

例子:

// Map starts out like this
1 -> value1
2 -> value2
3 -> value3
4 -> value4

// First 2 elements of array created
value1,value2

// Map is updated
3 -> value3
1 -> value1
2 -> value2
4 -> value4

// Last 2 elements of array created
value1,value2,value1,value4

如您所见,如果地图未更改,则数组将为:“value1,value2,value3,value4”或“value2,value3,value1,value4”(如果在更新后运行)。

这是一些示例代码,它不是原始代码,但应该可以解释我的问题:

Map<Integer, String> mapThatGetsUpdated = new ConcurrentHashMap<Integer, String>();
String[] array = new String[mapThatGetsUpdated.size()];

for (int i = 0; i < mapThatGetsUpdated.size(); i++) {
    array[i] = mapThatGetsUpdated.get(i);
}

这样做的明显方法是复制地图,然后在使用后将其丢弃,但我想要另一种方法,因为地图可能非常大,并且可能必须每秒复制几次。

【问题讨论】:

  • 你可以在更新或读取哈希图时锁定(同步)它吗?
  • HashMap 不维护插入顺序。
  • @Braj 编辑了帖子以使其更加清晰,我正在使用数组中索引的键。
  • @immibis 我认为这可能会破坏ConcurrentHashMap 的目的?

标签: java multithreading


【解决方案1】:

这是一个问题,因为尽管ConcurrentHashMap 是线程安全的,但您正在执行复合操作而没有锁定。

如果你想保证一致的状态,你必须防止边读边写。

如果您希望读者在整个读取操作中看到相同的信息,那么您必须这样做;

  • 在读取期间阻止写入
  • 在开始时创建正在读取的实体的快照

选项 1(不要这样做)

在读写过程中用synchronized锁定Map,一次只允许一个发生。

这是许多新手程序员的第一选择。它将有效地消除拥有ConcurrentHashMap 的任何好处。

选项 2(不要这样做)

在阅读之前复制Map。

注意到迭代可能会做一些除了循环之外的事情,您尝试通过仅复制 synchronized 块中的映射并使用副本来最小化读取锁定。

因为您经常阅读,所以这很可能也无济于事。

选项 3

使用AtomicReference。将阅读器中的引用复制到本地引用。作者将引用替换为全新的Map。

此选项的可行性取决于正在发生的写入量。它被称为Copy on Write,如果写入少而读取多,则可以带来良好的吞吐量。

它不需要显式锁定,但确实需要编写者做更多的工作。

选项 4

使用ReentrantReadWriteLock。将Map 更改为HashMap 并自己控制并发(编写一个包装类)。

使用ReadLock 锁定读取,使用WriteLock 锁定写入。您可以有很多读者,但一次只能有一位作者。

这将保证一致的读取。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多