ConcurrentHashMap是有Segment数组结构和HashEntry数组结构组成。
Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;
HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。
Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment包含一个HashEntry数组,
每个HashEntry是一个链表结构的元素,每一个Segment守护着一个HashEntry数组里的元素,当对
HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁。
1 /** 2 * 3 * @Description 4 * 1.线程不安全的HashMap 5 * HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的 6 * Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空, 7 * 就会产生死循环获取Entry。 8 * 2.效率低下的HashTable 9 * HashTable容器会使用synchronized来保证线程安全,但是在线程竞争激烈的情况下 10 * HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable 11 * 的同步方法时,会进入阻塞或轮询状态。如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素, 12 * 也不能使用get方法来获取元素,所以竞争越激烈效率越低。 13 * 3.ConcurrentHashMap的分段技术可有效提升并发访问率 14 * HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程 15 * 都必须竞争同一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程 16 * 访问容器不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问效率,这就是 17 * ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁, 18 * 当一个线程占用锁访问其中一个段数据的时候,其他段数据也能被其他线程访问。 19 * @throws Exception 20 */ 21 @Test 22 public void testHashMap() throws Exception { 23 final HashMap<String, String> map = new HashMap<String, String>(2); 24 25 Thread t = new Thread(new Runnable() { 26 27 @Override 28 public void run() { 29 for(int i=0; i<100000;i++){ 30 new Thread(new Runnable() { 31 32 @Override 33 public void run() { 34 map.put(UUID.randomUUID().toString(), ""); 35 } 36 }, "ftf"+i).start(); 37 } 38 39 } 40 }, "ftf"); 41 42 t.start(); 43 t.join(); 44 }
来源:《Java并发编程的艺术》
IT技术和行业交流群 417691667