Java 关于并发hashmap的内部工作

Java 关于并发hashmap的内部工作,java,map,concurrenthashmap,Java,Map,Concurrenthashmap,我正在浏览ConcurrentHashMap和,有一些问题 文章中提到,ConcurrentHashMap允许多个读卡器在没有任何阻塞的情况下并发读取。这是通过基于并发级别将映射划分为不同部分并在更新期间仅锁定映射的一部分来实现的。默认并发级别为16,因此映射被分为16个部分,每个部分由不同的锁管理。这意味着,16个线程可以同时在Map上运行,直到它们在Map的不同部分上运行为止。这使得ConcurrentHashMap在保持线程安全性不变的情况下仍具有高性能。不过,它附带了一个警告:由于像pu

我正在浏览
ConcurrentHashMap
和,有一些问题

  • 文章中提到,
    ConcurrentHashMap
    允许多个读卡器在没有任何阻塞的情况下并发读取。这是通过基于并发级别将映射划分为不同部分并在更新期间仅锁定映射的一部分来实现的。默认并发级别为16,因此映射被分为16个部分,每个部分由不同的锁管理。这意味着,16个线程可以同时在Map上运行,直到它们在Map的不同部分上运行为止。这使得
    ConcurrentHashMap
    在保持线程安全性不变的情况下仍具有高性能。不过,它附带了一个警告:由于像
    put()
    remove()
    putAll()
    clear()
    这样的更新操作不同步,因此并发检索可能不会反映地图上的最新更改

  • 文章中还提到了另一点:另一个需要记住的要点是CHM上的迭代,
    keySet
    返回的迭代器是弱一致的,它们只反映某一点上的
    ConcurrentHashMap
    状态,可能不反映任何最近的更改


  • 我还不明白用粗体突出显示的要点,你能提供更多信息或用一个简单的程序给我看吗?

    这里真正的问题是,当多个线程在愚弄一个数据结构时,线程不一定会步调一致

    一个线程正在读取user1。一个线程正在为user2编写。两个线程都无法预测另一个线程在各自进程中的位置。此外,我们无法为用户预测这两个过程的完成情况。如果写入操作首先更新数据,则读取操作将显示更新状态,即使user1可能在稍早的时候请求读取

    迭代时读取或修改的工作方式与此相同,但需要额外考虑的是,移动到下一个(迭代时)的过程本质上成为对映射状态的“读取”操作,如果不是映射中任何特定数据的内容的话

    因此,当您在这些数据结构中允许并发性时,您最终将得到一个“足够接近”的时间测试。(这与数据库的考虑因素非常相似,只是我们习惯于以这种方式思考数据库,时间框架是10个不同因素的组合

    注:要对@Matts在另一个答案中显示的精彩小时间线发表评论

    时间线显示了两个线程以及每个线程的开始和停止。两个线程的开始可以按(a,b)或(b,a)的顺序进行。结束可以按任意顺序进行,因为您不知道操作需要多长时间。这给出了两个线程开始和结束的4种方式。(a先开始先结束,a先开始先结束,b先开始先结束,b先开始先结束)现在…想象一下20个线程都在做同样的事情,比如说,响应20个最终用户提交的请求。有多少种可能的方式可以工作

  • 由于诸如put()、remove()、putAll()或clear()之类的更新操作未同步,因此并发检索可能不会反映地图上的最新更改

    正如我所理解的,这意味着一个线程中的映射的修改可能不必在另一个线程中同时发生的检索中看到。请考虑下面的例子:

                      Thread 1 starts              Thread 1's call to get("a")
                     a call to get("a")             completes, returning null
                             |                                 |
    Thread 1        ---------+---------------------------------+-------
                                 time ----->
    Thread 2        -----+---------------------------+-----------------
                         |                           |
                 Thread 2 starts a            Thread 2's call to
                call to put("a", 1)          put("a", 1) completes
    
    即使线程2
    put
    map线程1的
    get
    执行完成,线程1也没有“看到”映射修改,并返回
    null

  • 另一个需要记住的要点是CHM上的迭代,ConcurrentHashMap键集返回的迭代器是每周一致的,它们只反映ConcurrentHashMap的状态和某个点,可能不会反映最近的任何更改

    这是一种类似的情况。如果线程1从
    ConcurrentHashMap
    键集
    中获得一个
    迭代器
    ,并且稍后的线程2在映射中放入一个新条目,则线程1的
    迭代器
    不能保证看到该条目。(它可能看到,也可能不看到。)


  • 这就回答了你的第一个问题:你能不能也展示一个小程序,让你的理解更加清晰?这不是真的。这个问题只是偶尔出现,而且只有在不同线程几乎同时发生的情况下才会出现。你不能编程,因为它将取决于处理器的速度和线程的数量CPU可供运行代码的JVM使用。我应该指出,我过去曾尝试编写低级单元测试来导致这种情况发生。在这种情况下,线程之间的不一致是一个错误。我从未找到一个好方法来实现它。有时,在给定的特定进程中,您可以在很小的时间内实现它或者配置。然后测试包括重复测试X次,并期望它在Y%的时间内失败。一旦测试机器换成另一台,它就会停止。但即使在这之前,进行更像政治投票的测试也不是很令人满意。