2013-02-27 34 views
4

我正在经历ConcurrentHashMapthis related tutorial,并且有一些问题。关于并发hashmap的内部工作

  1. 在这篇文章中,有人提到,ConcurrentHashMap允许多个读者没有任何阻挡同时读取。这是通过将Map分成基于并发级别的不同部分以及在更新过程中仅锁定一部分Map来实现的。缺省并发级别为16,因此Map分为16部分,每部分由不同的锁定进行管理。这意味着,16个线程可以同时在Map上操作,直到它们在Map的不同部分上运行。尽管保持了线程安全性,但这使得ConcurrentHashMap具有高性能。虽然,它带有一个警告:这样以来put()remove()putAll()clear()更新操作不同步,并发检索可能不反映在地图上

  2. 另外一点,文章中也提到了最近的变化: 另一个要记住的重点是对CHM的迭代,由keySet返回的迭代器是弱一致的,它们只反映ConcurrentHashMap的状态,并不一定反映任何最近的变化

我还没有理解以粗体突出显示的几点,你能否提供更多信息或者在简单的程序中显示我?

+0

这给了回答您的第一个问题:http://stackoverflow.com/questions/14947723/is-concurrenthashmap-totally-safe/14947818#14947818可以 – 2013-02-27 17:27:58

回答

0

这里真正的问题是,当多个线程与数据结构混搭时,线程不一定会在锁定步骤中前进。

一个线程正在读取user1。一个线程正在为user2写入内容。这两个线程都不能预测其他线程将在各自进程中的位置。此外,我们无法预测用户对这两个流程完成的任何排序。如果写入首先更新数据,则即使user1可能稍早请求读取,读取也会显示更新的状态。

读取或修改迭代时以相同的方式工作,并考虑到移动到下一个(迭代时)的过程基本上成为对Map状态的“读取”操作,如果不是任何内容其中的特定数据。

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

注意:要对由@Matts在另一个答案中...

时间线显示了两个线程以及每个线程的开始和结束,两个线程的开始可以按照(a,b)或(b,a)的顺序出现。结束可以以任何一种顺序发生,因为你不能说出操作需要多长时间,这给出了两种线程可以开始和结束的4种方式(a先开始并结束,a先开始,b先结束,b开始,首先结束,b首先开始,b结束首先)现在...想象20个线程所有做同样的事情回应,比如说, 20个最终用户提交这个和那个请求。它可以工作多少种可能的方式。

+0

请你也会显示一个小程序,也会让理解更清晰 – user2094103 2013-02-27 17:36:43

+0

不是。这个问题偶尔会出现,只有当事情发生在不同线程的同时。你不能编程,因为它取决于处理器的速度以及运行代码的JVM可用的CPU数量。 – 2013-02-27 19:26:08

+1

我应该指出,我过去一直试图编写低级别的单元测试来引发这样的事情。在这种情况下,线程之间的不一致就是一个错误。我从来没有找到一个好方法来做到这一点。给定特定的处理器配置时,有时可以使其发生一小部分时间。然后测试包括重复测试X次,并期望它在Y%的时间内失败。一旦测试机换出另一台......它就会退出。但即使在这一点上,它的测试更像是一次政治民意调查,这并不令人满意。 – 2013-02-27 19:35:20

2
  1. 因为就像放(),删除()的putAll()或明确()更新操作不同步,并发检索可能无法反映最新的地图

    变化我理解它,这意味着在一个线程中对地图的修改可能不一定会在另一个线程中同时发生检索。请看下面的例子:

        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在地图线程1的get完成执行,线程1的值没有“看”地图的修改,并返回null

  2. 另一个重要的一点要记住的是遍历所有CHM,迭代器返回由ConcurrentHashMap中的keySet的每周都是一致的,它们只是反映了一个ConcurrentHashMap的状态,某一点而不代表任何最近的变化。

    这是一个类似的情况。如果线程1从ConcurrentHashMapkeySet获得Iterator,并且稍后线程2在地图中放入新条目,则线程1的Iterator不保证能看到该条目。 (可能也可能不会)。