Map实现线程安全的3种方式

2年前 (2022) 程序员胖胖胖虎阿
182 0 0

方式一、使用HashTable

        Map<String, String> hashtable = new Hashtable<>();

实现原理是在增删改查的方法上使用了synchronized锁机制,在多线程环境下,无论是读数据还是修改数据,在同一时刻只能有一个线程在执行synchronized方法(所有线程竞争同一把锁),因为对整个表进行锁定。所以线程越多,对该map的竞争越激烈,效率越低。


方式二、使用Collections.synchronizedMap

        Map<String, String> synchronizedHashMap =

                        Collections.synchronizedMap(new HashMap<String, String>());

       调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized同步关键字来保证对Map的操作是安全的。

        实现原理是使用工具类里的静态方法,把传入的HashTable包装成同步的,即在增删改查的方法上增加了synchronized锁机制,每次操作hashmap都需要先获取到这个对象锁,这个对象锁加了synchronized修饰,其实现方式和HashTable差不多,效率也很低。


方式三、使用ConcurrentHashMap

      Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

        实现原理是HashTable是对整个表进行加锁,而ConcurrentHashMap是把表进行分段,初始情况下分成16段,每一段都有一把锁。当多个线程访问不同的段时,因为获取到的锁是不同的,所以可以并行访问。效率比HashTable

ConcurrentHashMap的实现——JDK7版本

🧸分段锁机制

Map实现线程安全的3种方式 

左边是Hashtable的实现方式——锁整个hash表;右边是ConcurrentHashMap的实现方式——锁桶(或锁段)。

ConcurrentHashMap在对象中保存了一个Segment数组,即将hash表分成16个桶(默认值),而每个Segent元素即每个桶类似于一个Hashtable,诸如get、put、remove等常用操作只锁当前需要用到的桶。原来Hashtable只能一个线程进入,现在能同时16个进程进入(写进程才需要锁定,而读进程几乎不受限制)。

ConcurrentHashMap的实现——JDK8版本

在JDK1.7之前,ConcurrentHashMap是通过分段锁机制来实现的,所以其最大并发度受Segment的个数限制。因此,在JDK1.8中,ConcurrentHashMap的实现原理摒弃了这种设计,而是选择了与HahsMap蕾丝的数组+链表+红黑树的方式实现,而加锁则采用CAS和synchronized实现。

🧸CAS原理

       一般地,锁分为悲观锁和乐观锁:

       悲观锁对于同一个数据的并发操作,一定是为发生修改的。

       乐观锁对于同一个数据的并发操作是不会修改的,在更新数据时会采用尝试更新不断重试的方式更新数据。

CAS(Compare And Swap,比较交换):

        CAS有三个操作数,内存值V、预期值A、要修改的新值B,当且仅当预期值A 和 内存值V 相等时(A=V)才会将 内存值V修改为 要修改的新值B,否则什么都不做。

🧸ConcurrentHashMap的初始化

        在构造ConcurrentHashMap时,并不会对hash表进行初始化,hash表的初始化是在插入第一个元素时进行的。在put操作时,如果检测到table为空或长度为0时,会调用initTable()方法对table进行初始化操作。

好啦,就先到这里吧~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信~

版权声明:程序员胖胖胖虎阿 发表于 2022年11月22日 下午5:16。
转载请注明:Map实现线程安全的3种方式 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...