线程安全的集合面试题

1、Hashtable和HashMap有什么区别
HashMap是线程不安全的(多线程环境下会出问题);
Hashtable是线程安全的(但效率低下);
Hashtable底层和哈希表一样,扩容因子是0.75,扩容倍率是2倍。
Hashtable一次只能执行一个线程(全表加锁),采取悲观锁(增善改的方法上都加了synchronized)保证了线程安全。

2、ConcurrentHashMap,JDK7版本跟JDK8版本有什么不同?
ConcurrentHashMap1.7版本:
创建对象
1、默认创建一个长度16,加载因子为0.75的大数组,但这个大数组一但创建无法扩容,所以加载因子是给小数组用的。
2、还会创建一个长度为2的小数组,把地址值赋值给0索引处。其他索引位置的元素均为null。
 插入元素
第一次插入新元素时,会根据键的哈希值来计算出在大数组中应存入的位置。
如果为null,则按照模板创建小数组,大数组只用来存放地址值。
o创建完毕,会进行二次哈希,计算出在小数组中应存入的位置。
o直接存入。
如果不为null,就会根据记录的地址值找到小数组。
o二次哈希,计算出在小数组中应存入的位置。
o如果需要扩容,则先将小数组扩容2倍。
o如果不需要扩容,则判断小数组的这个位置有没有元素。
如果没有元素,则直接存。
如果有元素,就会调用equals方法,比较属性值
如果equals为true,相同则不存;
如果equals为false,新元素替换老元素,老元素挂在新元素下面,形成哈希桶结构(链表)。
线程安全
1、用synchronized同步代码块形式保证线程安全,锁住大数组的一个地址值连同它的小数组。
2、最多同时访问16个线程,因为大数组只有16个哈希槽。

ConcurrentHashMap1.8版本:
区别1.7:
底层结构改变:
哈希表(数组会扩容)——【数组】+【链表】+【红黑树】
1.7是旧元素挂新元素下面(旧挂新——头插法);1.8是新元素挂在旧元素下面(新挂旧——尾插法)!
线程安全机制改变:结合CAS机制+synchronized同步代码块形式保证线程安全。

如果该索引为null,则利用cas算法,将本结点添加到数组中。(第一次添加用CAS算法)

如果该索引不为null,则利用volatile关键字获得当前位置最新的结点地址,新元素挂在旧元素下面。(因为1.8后有了红黑树,会自动进行排序调整,再调整链表头就浪费资源了,而旧版本需要后进先出)
(红黑树转化条件见HashMap底层)

有元素后,再对该元素进行操作时,会给头结点(第一个元素)做为锁对象,加上synchronized同步代码块(锁对象的方式),保证线程安全。

加载机制改变:懒加载——第一次添加元素时初始化数组。(添加元素时,判断数组是否为空,或者长度为0,如果是,就初始化数组)

你可能感兴趣的:(后端)