注:前四题都是在JDK1.7的基础上进行解答,部分图是从一些大牛博客上参考的。
参考资料:https://blog.csdn.net/jjc120074203/article/details/78625433
https://www.cnblogs.com/zq-boke/p/8654539.html
《Java并发编程的艺术》 方腾飞 魏鹏 程晓明
- 1、ConcurrentHashMap底层数据结构是什么?
答:底层数据结构是:数组+数组+链表
解释:
底层结构图解(图1-1):
剖析源码:
(1)ConcurrentHashMap属性:Segment<K,V> 类型的 segments数组;
(2)Segment类是ConcurrentHashMap的一个内部类,它含有一个HashEntry类型的数组table,所以每一个segment中都含有一个table数组,同时table中的每一个元素都是一个链表。所以底层数据结构是数组+数组+链表(就如底层结构图解所示)。
HashEntry内部类:
- 2、ConcurrentHashMap通过什么保证线程安全?
答:<1>分段锁机制;
Segment类继承了ReentrantLock,所以每一个Segment都相当于一个锁,如(图1-1)中,segments整体并没有加锁,每一个segments[x]加锁;
<2>put方法采用Segment内部类中的put方法,所以put方法是线程安全的。
补充:
ReentrantLock(可重入锁),顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平和非公平性选择。
公平性问题:如果在绝对时间,先对锁进行获取的请求一定先被满足,那么这个锁是公平的。反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。ReentrantLock提供了一个构造函数,能够控制锁是否是公平的。 - 3、HashTable和concurrentHashMap线程安全保证机制是否一样?
答:不一样。
(1) HashTable采用的是全局锁,对整个put()方法进行同步,用synchronized关键字进行修饰;
(2)ConcurrentHashMap采用的是分段锁机制,并没有对整个segments数组进行加锁,而是每一个segments[x]就是一个ReentrantLock,即对每一个segments[x]进行加锁,不属于全局锁。(结合图1-1和第二题可知) - 4、HashMap、Hashtable和ConcurrentHashMap区别是什么?
(1)继承:
HashMap:
Hashtable:
ConcurrentHashMap:
(2)底层数据结构:
HashMap:数组+链表
Hashtable:数组+链表
ConcurrentHashMap:数组+数组+链表
(3)属性:
HashMap: 允许一个键为null,允许多个值为null;
Hashtable: 无论键还是值都不允许为null;
ConcurrentHashMap:
(4)线程安全问题:
HashMap:非线程安全;
Hashtable:线程安全的,put方法被synchronized关键字修饰,锁住整张hash表,使其只被一个线程占用;
ConcurrentHashMap:线程安全的,采用分段锁机制,ConcurrentHashMap中则是 一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。 这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。。
(5)扩容方式:
HashMap:2倍扩容
Hashtable:2倍原容量加1
ConcurrentHashMap:2倍扩容,但是它只是对table[]数组进行扩容,不能对segments[]数组进行扩容。 - 5、ConcurrentHashMapJDK1.7 、1.8的区别有哪些?