HashMap一般有两个使用场景:
(1)用在方法内的局部变量时,由于局部变量属于当前线程级别的变量,其他线程不能够访问,所以此时也就不存在线程安全问题。
(2)当用在单例对象成员变量时:这时候多个线程过来访问的就是同一个HashMap了,对同一个HashMap操作时就要考虑线程安全问题
为了避免该线程安全问题,不能使用HashMap作为成员变量,要寻求安全的Map,有如下几个选择:
(1)Hash Table
privateMapmap = newHashtable<>()
Hash Table源码中发现,其get/put方法均被synchronized关键词修饰(该关键词修饰代表这个方法加上了同步锁,相当于任意线程运行到该方法时,首先应检查有没有其它线程在使用该方法,有的话要等待上一个线程使用完毕后,当前线程才能使用)
正因如此,Hash Table的线程安全是基于方法级阻塞制的,它们占用共享资源,所以导致同时只能有一个线程操作get或put,并且get和put操作不能同时执行,所以这种同步的集合效率很低,一般不建议使用这个集合。
(2)SynchronizedMap
privateMapmap = Collections.synchronizedMap(newHashMap())
这种事直接使用工具类里面的方法创建synchronizedMap,把传入的HashMap对象进行了包装而已。
这个同步方式实现也比较简单,看出SynchronizedMap的实现方式是加了个对象锁,每次对HashMap的操作都要先获取这个mutex对象才能进入,故而性能也不会比HashTable好到哪去,也不建议使用。
(3)ConcurrentHashMap
privateMapmap = new ConcurrentHashMap<>()
这个实现结构最复杂,但同时也是效率最高最推荐使用的线程安全的Map,每个版本实现方式不一。Jdk8之前是使用分段加锁的方式,分成16个桶,每次只加锁其中一个桶,而jdk8又加入了红黑树和CAS算法来实现。
网友评论