我正在浏览ConcurrentHashMap
和this related tutorial,并提出了一些问题。
在文章中,提到ConcurrentHashMap
允许多个读者在没有任何阻塞的情况下同时读取。这是通过基于并发级别将Map划分为不同的部分并在更新期间仅锁定Map的一部分来实现的。默认并发级别为16,因此Map分为16个部分,每个部分由不同的锁控制。这意味着,16个线程可以同时在Map上运行,直到它们在Map的不同部分上运行。尽管保持线程安全完好,但这使ConcurrentHashMap
具有高性能。但是,它有一个警告:由于put()
,remove()
,putAll()
或clear()
等更新操作未同步,因此并发检索可能无法反映最新情况更改地图
文章中还提到了另一点:要记住的另一个要点是迭代CHM,keySet
返回的迭代器是弱一致的,它们仅反映ConcurrentHashMap
的状态某一点,可能不会反映最近的任何变化。
我没有理解以粗体突出显示的要点,您能提供更多信息或在简单的程序中向我展示吗?
答案 0 :(得分:2)
由于put(),remove(),putAll()或clear()等更新操作未同步,因此并发检索可能无法反映Map上的最新更改
据我所知,这意味着在一个线程中修改地图可能不一定会在另一个线程中同时发生检索。请考虑以下示例:
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
。
要记住的另一个要点是迭代CHM,ConcurrentHashMap的keySet返回的Iterator是每周一致的,它们只反映ConcurrentHashMap的状态和某些点,可能不反映最近的任何变化。
这是类似的情况。如果线程1从Iterator
的{{1}}获得ConcurrentHashMap
,并且稍后线程2在地图中放入新条目,则线程1的keySet
不能保证看到条目。 (可能或不可能。)
答案 1 :(得分:0)
这里真正的问题是,当多个线程愚弄数据结构时,线程不一定会在锁定步骤中进行。
一个线程正在为user1读取。一个线程是为user2写的。两个线程都不能预测其他线程将在其各自的进程中的位置。此外,我们无法预测用户对这两个过程完成的任何排序。如果写入首先更新数据,则即使user1可能稍早请求读取,读取也将显示更新的状态。
在迭代工作时以相同的方式读取或修改,另外考虑移动到下一个(迭代时)的过程基本上变成对Map的状态的“读取”操作,如果不是任何特定的内容其中的数据。
因此,当您在这些数据结构中允许并发时,您最终会在时间上进行“足够接近”的测试。 (这与数据库的相同考虑很像,除了我们习惯于以这种方式思考数据库,时间框架是10个不同的几个因素。
注意:在另一个答案中对@Matts显示的精彩小时间线发表评论......
时间轴显示两个线程以及每个线程的启动和停止。两个线程的开始可以按顺序(a,b)或(b,a)发生。结束可以以任何顺序发生,因为您无法判断操作需要多长时间。这提供了两种线程可以开始和结束的4种方式。 (a首先开始,然后结束,首先开始,b先结束,b首先开始,a先结束,b先开始,b先结束)现在......想象20个线程都做同样的事情,比如说, 20个最终用户提交此请求和那个。有多少种可能的方法。