java - 数据行级别的并发

标签 java java-6

我有一个HashMap并且想要单独同步每一行/条目以最大化并发性,因此这样许多线程可以同时访问HashMap但没有两个或更多线程可以同时访问同一行/条目。

我在代码中执行了以下操作,但我不确定它是否正确:

/* Lock/synchronize the data to this key, (skey is a key of type String) */
synchronized (aHashMap.get(skey)) {

    /* write the data (data is Integer) */
    aHashMap.put(skey, data);

}

最佳答案

适当的解决方案很大程度上取决于您的具体问题。如果所有线程都可以更新 Map 中的任何条目,那么首先要尝试的是 ConcurrentHashMap :

在这种情况下,您描述的操作将替换为:

data = ... compute ...
aHashMap.replace(skey, data);

使用 ConcurrentHashMap 解决了数据竞争,但仍然存在一个问题。如果另一个线程同时更新相同的 key ,则其中一项计算将会丢失。如果您对此感到满意,那就太好了。否则,您可以:

do {
  oldData = aHashMap.get(skey);
  data = ... compute (maybe based on oldData) ... 
  boolean success = aHashMap.replace(skey, oldData, data);
} while(!success);

在这种情况下,仅当数据未更改时替换才会成功(并且替换是原子的)。如果失败,您可以将所有内容放入 do while 循环中重试,也许基于更新的值。

此外,请注意 map 获取和替换之间不要产生任何副作用。该计算应该只创建一个全新的“数据”对象。如果您更新“oldData”对象或其他一些共享数据,您将得到意想不到的结果。

如果确实有副作用,一种方法是像这样创建键级锁:

synchronized(skey) {
  data = ... compute ... 
  aHashMap.replace(skey, data);
}

即使在这种情况下,仍然需要ConcurrentHashMap。此外,这不会阻止其他一些代码更新 map 中的该键。所有更新 key 的代码都需要锁定它。

此外,如果您更新“...compute...”中的 oldData 并且这些值在 map 中不唯一,则这将不是线程安全的。如果您确实想在那里更新 oldData,请用另一个同步覆盖它。

如果这确实有效并且您的内容与性能相符,那么就不用再犹豫了。

如果线程只更新值,而不更改键,那么您可以尝试将对转换为对象并使用与 Map 不同的东西。例如,您可以将对象集拆分为多个集合,然后将它们提供给线程。或者也许使用 ParallelArray。但我可能在这里离题了......:)

关于java - 数据行级别的并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7700303/

相关文章:

java - 将用户添加到聊天室 GUI 中的 JList

java - 递归方法给我错误

sdk - SDK开发时如何选择JDK版本?

java - JDK 1.7 vs JDK 1.6 内部类继承区别

java - 将 Java 1.6 设置为 Mac OS X 10.5.8 上的默认设置

xamarin - 在 Xamarin 中找不到 Android SDK

java - 从字符串缓冲阅读器中读取所有行

java - 使用扫描仪扫描字数和行数 (Java)

java - 打印所有 JVM 标志

Java 枚举 : implementing common methods, 避免重复