concurrenthashmap允许null值,concurrenthashmap可以存储null和n

http://www.itjxue.com  2023-01-20 01:01  来源:未知  点击次数: 

hashmap和concurrenthashmap的区别是什么?

最大的区别就是ConcurrentHashMap是线程安全的,hashMap不是线程安全的。

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与?Hashtable?大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。

重要参数

HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。

当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。在Java编程语言中,加载因子默认值为0.75,默认哈希表元为101。

hashmap键值可以为null吗

hashmap键值可以为null,设置方法为:

1、通过HashMap的put方法可以向变量中添加值。put方法有两个参数,就是要添加的数据的键和值。如图简单地通过循环向变量中添加值。

2、通过HashMap的get方法可以向变量中取值。get方法需要传入一个参数,那个参数是键值,在HashMap中,键值是唯一的。如图是取键值为2时对应的值。

3、遍历HashMap变量不能像遍历数组那样直接通过索引值来遍历,可以通过HashMap的keySet()方法获取变量中存放的所有键值,然后再通过遍历键获取对应的值。具体代码如图。

4、在HashMap中,想修改其中存放的值,可以通过使用put方法,将原有的值覆盖。如图是通过覆盖原有键为2的值来修改变量中存放的内容。

ConcurrentHashMap面试问题总结

Q:ConcurrentHashMap和HashMap的区别是什么?

A:

1.ConcurrentHashMap是线程安全的,HashMap是线程不安全的

2.ConcurrentHashMap不允许Key为null的值插入。而HashMap是可以的

Q:JDK8的ConcurrentHashMap和JDK7的ConcurrentHashMap有什么区别?

A:

1.JDK7的ConcurrentHashMap采用分段锁的策略,将整个数组分成多个segment,对这些segment进行分段加锁,使用的锁是ReentrantLock。而JDK8中的ConcurrentHashMap不采用分段锁的方式,直接使用Synchronized来进行。

2.JDK8中的ConcurrentHashMap新增了红黑树,并且插入用的是尾插法。

3.JDK7中的ConcurrentHashMap进行扩容时,是对当前的segment进行扩容,不会对其他segment有影响。而JDK8中就跟HashMap一样。整体扩容,但是保证线程安全

Q:JDK7中的ConcurrentHashMap是如何扩容的

A:JDK7中对每一段segment进行扩容,每一段segment的扩容跟HashMap保持一致

Q:JDK8中的ConcurrentHashMap是如何扩容的

A:在扩容时,首先会生成一个双倍大小的数组,生成完数组后,线程就会开始转移元素,在扩容的过程中,如果有其他线程在put,那么这个put线程会帮助去进行元素的转移,虽然叫转移,但是其实是基于原数组上的Node信息去生成一个新的Node的,也就是原数组上的Node不会消失,因为在扩容的过程中,如果有其他线程在get也是可以的。

Q:ConcurrentHashMap是如何保证线程安全的

A:

1.在JDK7的时候。ConcurrentHashMap是通过ReentrantLock+CAS+分段思想来保证的并发安全的,在JDK7的ConcurrentHashMap中,首先有一个Segment数组,存的是Segment对象,Segment相当于一个小HashMap,Segment内部有一个HashEntry的数组,也有扩容的阈值,同时Segment继承了ReentrantLock类,同时在Segment中还提供了put,get等方法,比如Segment的put方法在一开始就会去加锁,加到锁之后才会把key,value存到Segment中去,然后释放锁。同时在ConcurrentHashMap的put方法中,会通过CAS的方式把一个Segment对象存到Segment数组的某个位置中。同时因为一个Segment内部存在一个HashEntry数组,所以和HashMap对比来看,相当于分段了,每段里面是一个小的HashMap,每段公用一把锁,同时在ConcurrentHashMap的构造方法中是可以设置分段的数量的,叫做并发级别concurrencyLevel.

2.在JDK8的时候,ConcurrentHashMap是通过synchronized+cas来实现了。在JDK8中只有一个数组,就是Node数组,Node就是key,value,hashcode封装出来的对象,和HashMap中的Entry一样,在JDK8中通过对Node数组的某个index位置的元素进行同步,达到该index位置的并发安全。同时内部也利用了CAS对数组的某个位置进行并发安全的赋值。

ConcurrentHashMap常问问题

采用了分段锁的思想,将哈希桶数组分成一个个的Segment数组(继承ReentrantLock),每一个Segment里面又有多个HashEntry,也是被volatile修饰的,是为了保证在数组扩容时候的可见性,HashEntry中又有key,hash,value,next属性,而value,next又是被volatile修饰为了保证多线程环境下数据修改时的可见性,多线程环境下ConcurrentHashMap会对这些小的数组进行加锁,这样多线程操作Map就相当于是操作单线程环境下的HashMap,比如A线程对其中一个段进行写操作的时候线程B就不能对其进行写操作,但是线程B可以对其他的段进行写操作,从而实现并发修改和访问。

JDK1.8的ConcurrentHashMap摒弃了分段锁的思想,采用jdk1.8中HashMap的底层机构,Node数组+链表+红黑树。Node是继承了Entry的一个内部类,他的value和next都是被volatile修饰的原因也是为了保证多线程下修改数据的可见性。

采用CAS+synchronized实现更加细粒度的锁,将锁的级别控制在更细粒度的哈希桶数组元素的级别,只需要锁住链表头节点(红黑树的根节点)就不会影响到其他哈希桶数组元素的读写,大大的提高了并发度。

是不需要加锁的,因为Node节点使用了volatile修饰了value和next节点,而在jdk8中同样也是使用了volatile修饰了value和next节点,这样保证可见性了就不需要加锁了。

key不能为空,无法解释,没有什么可说的,可能就是作者的想法。

value不能为空是因为ConcurrentHashMap是工作在多线程环境下的,如果调用get方法,返回null,这个时候就存在二义性,因为ConcurrentHashMap不知道是没有这个key,还是这个key对应的值是不是null。所以干脆不支持value为null。

HashMap的迭代器是强一致性的,而ConcurrentHashMap的迭代器是弱一致性的,因为在多线程环境下,在创建迭代器的过程中,内部的元素会发生变化,如果是在已经遍历过去的数据中发生变化,迭代器是无法反映出来数据发生了改变,如果是发生在未迭代的数据时,这个时候就会反映出来,强一致性就是说只要迭代器创建出来之后数据就不会发生改变了。这样设计的好处就是迭代器线程可以使用原来的老数据进行遍历,写线程可以并发的完成改变,这样就保证了多个线程执行的时候的连续性和可拓展性,提升了并发性能。

JDK1.7中,并发度就是ConcurrentHashMap中的分段个数,即Segment[]数组的长度,默认是16,这个值可以在构造函数中设置。如果自己设置了并发度那么就会和HasHMap一样会去找到大于等于当前输入值的最小的2的幂指数作为实际并发度。如果过小就会产生锁竞争,如果过大,那么就会导致本来位于同一个Segment的的访问会扩散到不同的Segment中,导致性能下降。

JDK1.8中摈弃了Segment的概念,选择使用HashMap的结构,并发度依赖于数组的大小。

ConcurrentHashMap效率高,因为hashTable是给整个hash表加锁,而ConcurrentHashMap锁粒度要更低。

使用Collections.synchronizedMap(Map类型的对象)方法进行同步加锁,把对象转换为SynchronizedMapK,V类型。其实就是对HashMap做了一次封装,多个线程竞争一个mutex对象锁,在竞争激烈的情况下性能也非常差,不推荐使用。

(责任编辑:IT教学网)

更多

推荐CorelDraw教程文章