并发容器
- 为什么需要并发容器
java提供了很多丰富的容器,主要可以分为四大类别List、Map、Set、Queue,但是这些容器的实现类有各种各样的缺点,如我们常见的ArrayList、HashMap是线程不安全的,如Hashtable、Vector是线程安全,但是效率很差,这些线程安全的容器是通过synchronized关键字来实现的,这样做就削弱了程序的并发性,所以并发容器就诞生了。
- 并发容器的定义
并发容器是专门针对多线程并发设计的,使用了锁分段技术,只对操作位置进行同步操作,但是其它其它线程可以对没有操作的位置继续进行访问。
1、ConcurrentHashMap
1.1数据结构
ConcurrentHashMap是java.util.concurrent(JUC)包下的成员,它支持线程安全、支持多线程操作时并发读写操作,在jdk1.7中,它采用的是Segment分段锁,但是在1.8中,使用的是CAS+synchronized,底层使用的是数组+链表+红黑树
- jdk1.7
jdk1.7中的ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成,Segment是一种可重入锁(ReentrantLock),每个Segment都包含一个HashEntry[]数组,每个HashEntry是一个链表结构,当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁。ConcurrentHashMap使用锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据分配一把锁,当一个线程占用锁访问其中一个段数据的时候,其它段的数据也能被其它线程访问。
- jdk1.8
jdk1.8抛弃了Segment分段锁,采用的是CAS+synchronized,它的改进是为了解决查询链表效率太低的问题,将1.7中的HashEntry改为node,取消的ReentrantLock改为了synchronized,采用的数据结构为数组+链表+红黑树,保证了查询效率。
1.2代码测试
为了验证ConcurrentHashMap效率如何,我们编写一个Demo来测试下,将它与Hashtable比较,Hashtable容器是使用synchronized来保证线程安全的。
对比代码测试
/**
* @Author: Simon Lang
* @Date: 2020/5/12 10:40
*/
public class test_concurrentHashMap {
public static void main(String[] args){
//使用HashTable测试
final Map<String,String> map=new Hashtable<String, String>();
//使用concurrentHashMap测试
// final Map<String,String> map=new ConcurrentHashMap<String, String>();
Thread[] array=new Thread[1000];
final Random r=new Random();
//定义一个门栓
final CountDownLatch latch=new CountDownLatch(array.length);
long begin=System.currentTimeMillis();
for(int i=0;i<array.length;i++){
array[i]=new Thread(new Runnable() {
@Override
public void run() {
for(int j=0;j<10000;j++){
map.put("key"+r.nextInt(1000000),"value"+r.nextInt(1000000));
}
latch.countDown();
}