【问题标题】:How to use and set appropriately concurrency level for ConcurrentHashMap?如何为 ConcurrentHashMap 使用和设置适当的并发级别?
【发布时间】:2013-08-28 12:38:52
【问题描述】:

我正在使用 concurrenthashmap 中的大约 1000 个元素。默认并发级别为 16 。谁能帮助我一些算法或因素,我可以从中确定适合我的场景的并发级别,或者并发级别以何种方式影响多个线程的处理。

   ConcurrentHashMap<String, String> map=new ConcurrentHashMap<String, String>(500,1,20);             

20 是我的并发级别(虚拟值)。需要有效地设置它

【问题讨论】:

  • 你知道有多少线程可以同时访问地图吗?
  • 取决于当前登录系统的用户数。

标签: java concurrency


【解决方案1】:

根据文档:

更新操作之间允许的并发性由 可选的 concurrencyLevel 构造函数参数 (默认16),用作内部调整大小的提示。这 表在内部分区以尝试允许指示的 无争用的并发更新数。因为安置 在哈希表中本质上是随机的,实际的并发将 变化。 理想情况下,您应该选择一个值来容纳尽可能多的 线程将同时修改表。使用 显着高于您需要的价值会浪费空间和时间, 并且显着降低的值会导致线程争用。

所以你需要回答1个问题:

同时修改表的线程数是多少?

【讨论】:

  • 如果答案是 100,那么您可以将并发级别设置为 100。
  • @Tala 假设我的系统有 100 个线程。并且 90 个线程将遍历此映射,并且最多 10 个线程将尝试一次修改(放置/删除)此映射,那么并发级别是多少?请回复。
  • 我会将它设置为 10,如果在您的情况下,没有线程在等待真的很关键,您可以设置为 16 例如。
【解决方案2】:

Java 8:

现在ConcurrentHashMap 根本不使用固定的锁分条方案,而是每个桶都用作使用内在同步的“分条”。

源代码:

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    ...
    Node<K,V> f; int n, i, fh;
    ...
    else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    ...
       synchronized (f) {
           ...
       }
}

而构造函数的参数只是将其用作文档所说的大小提示。

concurrencyLevel - 并发更新线程的估计数量。实现可以将此值用作大小提示。

来源:

public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    if (initialCapacity < concurrencyLevel)   // Use at least as many bins
        initialCapacity = concurrencyLevel;   // as estimated threads
    long size = (long)(1.0 + (long)initialCapacity / loadFactor);
    int cap = (size >= (long)MAXIMUM_CAPACITY) ?
        MAXIMUM_CAPACITY : tableSizeFor((int)size);
    this.sizeCtl = cap;
}

所以你不需要自己考虑,ConcurrentHashMap会为你处理。

【讨论】:

  • 从 cmets 到 java 8 ConcurrentHashMap 的实现:
  • 我们不想浪费将不同的锁对象与每个 bin 关联所需的空间,因此将 bin 列表的第一个节点本身用作锁。对这些锁的锁定支持依赖于内置的“同步”监视器。
【解决方案3】:

ConcurrentHashMap 允许多个读取器同时读取而不会阻塞。这是通过根据并发级别将 Map 划分为不同的部分并在更新期间仅锁定 Map 的一部分来实现的。默认并发级别为 16,因此 Map 分为 16 个部分,每个部分由不同的锁管理。这意味着,16 个线程可以同时对 Map 进行操作,直到它们对 Map 的不同部分进行操作。尽管保持线程安全不变,这使得 ConcurrentHashMap 具有高性能。

【讨论】:

    【解决方案4】:

    16 是地图将被分割成的默认区域数。 在读取线程的情况下,ConcurrentHashMap(几乎在所有情况下)完全没有锁定。 编写器线程数是您需要担心的事情。这个数字应该等于你拥有的区域数。

    【讨论】:

      【解决方案5】:

      所以,并发级别等于写入线程。并且map也会分段等于并发级别的值。

      【讨论】:

      • 对于 JDK8+,Segment 现在只是为了向后兼容。现在同步在桶级别,值对象本身被用作锁。
      猜你喜欢
      • 1970-01-01
      • 2017-01-14
      • 2017-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      相关资源
      最近更新 更多