第一部分:
常用的集合类ArrayList、Map等在多线程操作同一对象时会发生不同步的线程而造成数据读取和写入错误;通常都是采用synchronized修饰符或Lock将那些方法括起来来确保它们在执行时不会被其他线程打扰;java5线程并发库为我们提供了一些相对于普通集合类的线程安全类,来自动解决线程同步的问题,如ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArrayList等。
下面一个程序演示了CopyOnWriteArrayList相对ArrayList的线程安全性
曾经研究过jkd1.5新特性,其中ConcurrentHashMap就是其中之一,其特点:效率比Hashtable高,并发性比hashmap好。结合了两者的特点。
集合是编程中最常用的数据结构。而谈到并发,几乎总是离不开集合这类高级数据结构的支持。比如两个线程需要同时访问一个中间临界区(Queue),比如常会 用缓存作为外部文件的副本(HashMap)。这篇文章主要分析jdk1.5的3种并发集合类型 (concurrent,copyonright,queue)中的ConcurrentHashMap,让我们从原理上细致的了解它们,能够让我们在深 度项目开发中获益非浅。
在tiger之前,我们使用得最多的数据结构之一就是 HashMap和Hashtable。大家都知道,HashMap中未进行同步考虑,而Hashtable则使用了synchronized,带来的直接 影响就是可选择,我们可以在单线程时使用HashMap提高效率,而多线程时用Hashtable来保证安全。
当 我们享受着jdk带来的便利时同样承受它带来的不幸恶果。通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁 住整张表让线程独占,安全的背后是巨大的浪费,慧眼独具的Doug Lee立马拿出了解决方案----ConcurrentHashMap。
ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁。如图
左 边便是Hashtable的实现方式---锁整个hash表;
而右边则是ConcurrentHashMap的实现方式---锁桶(或段)。
ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。
试想,原来 只能一个线程进入,现在却能同时16个写线程进入(写线程才需要锁定,而读线程几乎不受限制,之后会提到),并发性的提升是显而易见的。
更 令人惊讶的是ConcurrentHashMap的读取并发,因为在读取的大多数时候都没有用到锁定,所以读取操作几乎是完全的并发操作,而写操作锁定的 粒度又非常细,比起之前又更加快速(这一点在桶更多时表现得更明显些)。只有在求size等操作时才需要锁定整个表。而在迭代 时,ConcurrentHashMap使用了不同于传统集合的快速失败迭代器(见之前的文章《JAVA
API备忘---集合》)的另一种迭代方式,我们称为弱一致迭代器。
在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出 ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数 据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证
了多个线程并发执行的连续性和扩展性,是性能提升的关键。
接下来,让我们看看ConcurrentHashMap中的几个重要方法,心里知道了实现机制后,使用起来就更加有底气。
ConcurrentHashMap中主要实体类就是三个:
ConcurrentHashMap(整个Hash表),
Segment(桶),
HashEntry(节点),对应上面的图可以看出之间的关系。
get 方法(请注意,这里分析的方法都是针对桶的,因为ConcurrentHashMap的最大改进就是将粒度细化到了桶上),首先判断了当前桶的数据个数是
否为0,为0自然不可能get到什么,只有返回null,这样做避免了不必要的搜索,也用最小的代价避免出错。然后得到头节点(方法将在下面涉及)之后就 是根据hash和key逐个判断是否是指定的值,如果是并且值非空就说明找到了,直接返回;程序非常简单,但有一个令人困惑的地方,这句return readValueUnderLock(e)到底是用来干什么的呢?研究它的代码,在锁定之后返回一个值。但这里已经有一句V v = e.value得到了节点的值,这句return readValueUnderLock(e)是否多此一举?事实上,这里完全是为了并发考虑的,这里当v为空时,可能是一个线程正在改变节点,而之前的
get操作都未进行锁定,根据bernstein条件,读后写或写后读都会引起数据的不一致,所以这里要对这个e重新上锁再读一遍,以保证得到的是正确 值,这里不得不佩服Doug Lee思维的严密性。整个get操作只有很少的情况会锁定,相对于之前的Hashtable,并发是不可避免的啊!
put 操作一上来就锁定了整个segment,这当然是为了并发的安全,修改数据是不能并发进行的,必须得有个判断是否超限的语句以确保容量不足时能够
rehash,而比较难懂的是这句int index = hash & (tab.length - 1),原来segment里面才是真正的hashtable,即每个segment是一个传统意义上的hashtable,如上图,从两者的结构就可以看 出区别,这里就是找出需要的entry在table的哪一个位置,之后得到的entry就是这个链的第一个节点,如果e!=null,说明找到了,这是就 要替换节点的值(onlyIfAbsent == false),否则,我们需要new一个entry,它的后继是first,而让tab[index]指向它,什么意思呢?实际上就是将这个新entry
插入到链头,剩下的就非常容易理解了。
remove 操作非常类似put,但要注意一点区别,中间那个for循环是做什么用的呢?(*号标记)从代码来看,就是将定位之后的所有entry克隆并拼回前面去, 但有必要吗?每次删除一个元素就要将那之前的元素克隆一遍?这点其实是由entry的不变性来决定的,仔细观察entry定义,发现除了value,其他
所有属性都是用final来修饰的,这意味着在第一次设置了next域之后便不能再改变它,取而代之的是将它之前的节点全都克隆一次。至于entry为什 么要设置为不变性,这跟不变性的访问不需要同步从而节省时间有关,关于不变性的更多内容,请参阅之前的文章《线程高级---线程的一些编程技巧》
以上,分析了几个最简单的操作,限于篇幅,这里不再对rehash或iterator等实现进行讨论,有兴趣可以参考src。
接下来实际上还有一个疑问,ConcurrentHashMap跟HashMap相比较性能到底如何。这在Brian Goetz的文章中已经有过评测http://www.ibm.com/developerworks/cn/java/j-jtp07233/ 。
另外主题:Java并发编程之ConcurrentHashMap:可以借鉴:
http://www.iteye.com/topic/1103980。分析得也很好。
最后:java.util.concurrent介绍总结:
http://www.cnblogs.com/sarafill/archive/2011/05/18/2049461.html
分享到:
相关推荐
在并发处理上,JDK1.5引入了并发工具类(java.util.concurrent),包括线程池(ExecutorService)、并发容器(如ConcurrentHashMap)以及Future接口等,这些工具极大地提高了多线程环境下的程序设计效率和性能。...
9. **集合框架增强**:对Java集合框架进行了优化,包括更强大的List、Set和Map实现,以及新的并发集合类,如ConcurrentHashMap,适应多线程环境。 10. **异常链**:在处理异常时,可以创建异常链,记录并展示完整的...
- **多线程**:`java.lang.Thread`和`java.util.concurrent`包提供了多线程编程的支持,包括线程创建、同步控制和并发工具类。 - **网络编程**:`java.net`包提供了Socket和ServerSocket类,用于实现基于TCP/IP的...
1. 并发工具库的改进:包括`java.util.concurrent`包中更多线程安全的数据结构和并发工具,如`ConcurrentHashMap`,`Phaser`等。 2. 轻量级注解处理:允许在编译时处理注解,无需生成额外的源代码或字节码。 3. 动态...
【Java 多线程与并发】并发集合类`ConcurrentHashMap`是Java程序设计中一个重要的工具,尤其在高并发场景下,它提供了高效的线程安全。`ConcurrentHashMap`在JDK 1.7和1.8中有着显著的区别。 在JDK 1.7中,`...
Java多线程同步是Java编程中关键的并发概念,它涉及到如何在多个线程访问共享资源时保持数据的一致性和完整性。`java.util.concurrent`包是Java提供的一个强大的并发工具库,它为开发者提供了多种线程安全的工具,...
4. **并发集合**:讲解ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentLinkedQueue等并发安全的集合类,以及它们在多线程环境下的性能优势。 5. **线程通信**:介绍wait()、notify()和notifyAll()方法,以及...
3. **Java多线程与并发编程**:多线程是Java的一大优势,面试中会考察线程的创建、同步机制(synchronized、Locks)、线程池(ExecutorService)、并发工具类(如Semaphore、CyclicBarrier)等。理解线程安全问题及...
6. **并发集合**:如ConcurrentHashMap、CopyOnWriteArrayList等,这些集合在多线程环境下提供了安全的读写操作,提高了并发性能。 7. **线程中断与异常处理**:如何通过interrupt()方法中断线程,以及如何处理...
14. **多态实现**: 多态是面向对象的核心特性之一,通过继承和接口实现。子类对象可以被视为父类对象,调用父类中的方法。 15. **重写和重载**: 重写(Override)是指子类重写父类的同名方法,保持方法名和参数列表...
主要内容涵盖集合类同步的基础概念、不同同步策略的应用以及JDK 5.0引入的并发集合类。 #### 集合类的同步问题 在多线程编程中,当多个线程尝试同时修改或访问同一个集合对象时,可能会引发数据不一致或错误的行为...
综上所述,`ConcurrentHashMap`和`ConcurrentSkipListMap`都是为了解决多线程环境下的并发问题而设计的高效线程安全集合类。`ConcurrentHashMap`通过分段锁和细粒度锁的机制提高了并发性能,而`...
此外,对于多线程编程,JDK 1.8提供了更强大的并发工具类,如Fork/Join框架和ConcurrentHashMap的改进。 "使用说明.txt"文件通常包含关于如何解压、安装和使用JDK的详细指南。用户应当遵循这些说明来确保正确安装和...
Java JDK(Java Development Kit)是Oracle公司提供的用于开发和运行Java应用程序的软件工具包,而JDK6是Java的一个重要版本,发布于2006年。本篇将详细介绍Java JDK6的安装过程及其相关知识点。 一、Java JDK6概述...
10. **并发集合(Concurrent Collections)**:如`ConcurrentHashMap`, `CopyOnWriteArrayList`等并发集合,它们提供了线程安全的数据结构,以适应多线程环境下的高效并发操作。 11. **NIO(New Input/Output)**:...
这里提到的“多线程并发学习书籍”集合包含六本关于这一主题的专业书籍,覆盖了2012年至2018年的最新知识。下面,我们将深入探讨这些书籍可能涵盖的关键知识点。 1. **《Java高并发编程详解》2018版**:这本书可能...
综上所述,Java并发编程涵盖了许多关键概念和技术,包括多线程的创建与管理、内存模型的理解与应用、JDK并发包的使用、并发集合的选择与操作,以及显示锁和死锁的处理。掌握这些知识对于编写高效、可靠的多线程程序...
并发集合操作:展示如何在多线程环境下安全地操作集合类,如ConcurrentHashMap和CopyOnWriteArrayList。 线程生命周期管理:示例代码展示如何创建、启动、管理和终止线程。 线程通信:展示线程间的通信机制,包括...