java有一个"LinkedConcurrentHashMap"数据结构吗?

Pet*_*Lee 50 java data-structures

我需要一个LinkedHashMap的数据结构,并且是线程安全的.

我怎样才能做到这一点 ?

Yis*_*hai 29

您可以将地图包装在Collections.synchronizedMap中以获取维护插入顺序的同步散列映射.这不如ConcurrentHashMap有效(并且不实现ConcurrentMap的额外接口方法),但它确实为您提供了(某种程度上)线程安全的行为.

即使是强大的Google Collections似乎也没有解决这个特殊问题.但是,有一个项目确实试图解决这个问题.

我在某种程度上说同步,因为在并发修改异常可能发生的意义上,迭代仍然不是线程安全的.

  • +1.我只想补充一点,我记得它,它是不是这个原因还有是因为为了保持与访问顺序的行为将或多或少被迫反正锁定整个表(或必须在复杂的,这种方式可能会导致死锁或者速度慢,因此在普通的LinkedHashMap周围进行同步在大多数情况下会或多或少地同等有效.解释可能是粉饰,但对我来说是有道理的. (5认同)
  • (免责声明:我没有投票也没有投票).不喜欢这种方法.同步!=并发.如果你在地图上有一个密集的并行作业,你的n-1个线程将一直被阻止.虽然Concurrent应该避免在大多数情况下阻塞. (4认同)

hoh*_*uli 14

这个问题有很多不同的方法.你可以使用:

Collections.synchronizedMap(new LinkedHashMap());
Run Code Online (Sandbox Code Playgroud)

正如其他回复所暗示的那样,但这有几个问题你需要注意.最值得注意的是,在迭代集合时,您经常需要保持集合同步锁定,这反过来又会阻止其他线程访问集合,直到您完成对它的迭代.(参见Java理论与实践:并发集合类).例如:

synchronized(map) {
    for (Object obj: map) {
        // Do work here
    }
}
Run Code Online (Sandbox Code Playgroud)

运用

new ConcurrentHashMap();
Run Code Online (Sandbox Code Playgroud)

可能是一个更好的选择,因为您不需要锁定集合来迭代它.

最后,您可能想要考虑更具功能性的编程方法.那就是你可以认为地图本质上是不可变的.您可以创建一个包含旧地图内容和新添加内容的新地图,而不是添加到现有地图.这听起来很奇怪,但它实际上是Scala处理并发和集合的方式

  • 是的,除了 Scala 有一种更有效的方法来创建带有插入或删除元素的地图副本。如果我没记错的话,它使用平衡树,并在适用的情况下在旧树和新树之间共享子树节点。对此的 Java 实现可能必须复制整个树。 (2认同)

And*_*ich 8

一个实现在谷歌代码可用.来自他们网站的报价:

java.util.LinkedHashMap的高性能版本,用作软件缓存.

设计

  • 并发链表通过ConcurrentHashMap运行以提供驱逐顺序.
  • 支持插入和访问有序驱逐策略(FIFO,LRU和第二次机会).

  • ConcurrentLinkedHashMap 现在是 Caffeine https://github.com/ben-manes/caffeine (3认同)
  • 这个项目的主要提交者是谷歌软件工程师Ben Manes,所以它非常值得信赖(恕我直言). (2认同)

小智 7

您可以使用ConcurrentSkipListMap,仅在Java SE/EE 6或更高版本中可用.它是预先订购的,因为按键按照它们的自然顺序排序.您需要有一个比较器或使键可比对象.为了模仿链接的哈希映射行为(迭代顺序是添加条目的时间顺序),我实现了我的密钥对象总是比较大于给定的其他对象,除非它是相等的(无论你的对象是什么) ).包装的同步链接哈希映射是不够的,因为如 http://www.ibm.com/developerworks/java/library/j-jtp07233.html中所述:"同步集合包装器,synchronizedMap和synchronizedList有时称为条件线程-safe - 所有单个操作都是线程安全的,但控制流依赖于先前操作结果的操作序列可能会受到数据竞争的影响.清单1中的第一个代码段显示了常见的put-if-absent惯用语 - - 如果Map中还没有条目,请添加它.不幸的是,正如所写的那样,另一个线程可能在containsKey()方法返回的时间和put()之间插入一个具有相同键的值.调用方法.如果要确保一次插入一次,则需要使用在Map m上同步的synchronized块包装这对语句."

所以唯一有帮助的是ConcurrentSkipListMap,它比普通的ConcurrentHashMap慢3-5倍.

  • 不幸的是,定义插入顺序的自定义比较器不起作用.这假定传递给compare()的第一个对象是新插入的对象.但是ConcurrentSkipList映射在迭代时也会调用compare(至少使用`map.entrySet()`),在这种情况下,您不知道哪两个对象是先插入的对象.除非此对象包含创建日期 - 但是对于例如字符串的常见情况,这会失败. (2认同)

Dav*_*haw 4

Collections.synchronizedMap(new LinkedHashMap())

  • 但是,如果您想要执行任何复合操作(例如 ConcurrentHashMap 提供的额外操作),您仍然需要手动同步 (3认同)