JAVAEE中的线程安全的集合类(包括HashtableConcurrentHashMap)
文章目录
- 线程安全的集合类
- 一、多线程环境使用 ArrayList
- 二、多线程环境使用哈希表
- 1. Hashtable
- 2.ConcurrentHashMap(推荐使用)
- 总结
线程安全的集合类
标准库里面大部分的集合类,都是线程不安全的~~
少数几个线程安全的 : Vector,Stack,HashTable 虽然线程安全,但是其实不太推荐用
一、多线程环境使用 ArrayList
- 如果需要在多线程环境下保证集合类的线程安全,最简单的做法,就是自己加锁~
- 使用:
Collections.synchronizedList(new ArrayList);
其实就是套了一层壳,壳上加锁了~~
但是这个做法有点简单粗暴,就是无脑的给各种方法都加上锁… - 使用
CopyOnWriteArrayList
不加锁保证线程安全
适用的场景有限,一写多读,写的频率较低才比较适合用
CopyOnWrite: 写(修改)时拷贝(复制),就是写了什么就把它复制一份
这个操作适用范围非常有限~ 如果元素特别多/修改特别频繁,就不适合使用这种方式了~
但是这种写时拷贝的思想,很多地方都会用到~ 一个典型的,显卡给显示器渲染画面,也是类似的操作
二、多线程环境使用哈希表
HashMap
本身不是线程安全的.
在多线程环境下使用哈希表可以使用:
- Hashtable
- ConcurrentHashMap
1. Hashtable
只是简单的把关键方法加上了 synchronized
关键字,不推荐使用
因为最大的问题是属于Hashtable
无脑给各种方法加 synchronized
…
而加锁是需要因地制宜的,所以这种方法其实是不太合理的!
2.ConcurrentHashMap(推荐使用)
ConcurrentHashMap
背后做了很多优化策略!
优化策略:
- 锁粒度的控制
HashTable
直接在方法上加synchronized
,相当于是对this
加锁,而this
只是针对哈希表对象来加锁,一个哈希表只有一个锁…
意味着:
多个线程,无论这些线程都是如何操作这个哈希表,都会产生锁冲突!!HashTable:
多个线程,无论这些线程都是如何操作的这个哈希表,都会产生锁冲突了!
而
ConcurrentHashMap
做出了改进~
ConcurrentHashMap
不是一把锁,而是多把锁 ~
🎾给每个哈希桶都分配一把锁
此时只有当两个线程访问同一个哈希桶的时候,才有锁冲突~
如果不是同一个哈希桶,则没有锁冲突了!!
由于哈希桶个数很多~ 此时恰好两个线程操作同一个哈希桶的概率就大大降低了~~
ConcurrentHashMap
每个哈希桶都有自己的锁~
大大降低了锁冲突的概率,性能也就大大提高了!
-
ConcurrentHashMap
做了一个激进的操作~
❗只是给写操作加锁,读操作不加锁了
-
充分的利用到了
CAS
的特性
比如像维护元素个数,都是通过CAS
来实现,而不是加锁~
包括还有些地方直接使用CAS
实现的轻量级锁来实现~
虽然synchronized
内部已经有很多优化了,但是终究这里优化的是JVM
内部的~ 程序员是不可控的!
ConcurrentHashMap
思路就是能不加锁就不加~
ConcurrentHashMap 核心优化思路:进一切可能降低锁冲突的概率!!!! |
ConcurrentHashMap
对于扩容操作,进行了特殊的优化~ 化整为零~(有点像写时拷贝)ConcurrentHashMap
在扩容的时候,就不再是直接一次性完成搬运了,而是搬运一点~
分段锁:
旧版本的ConcurrentHashMap
实现方式(从Java8就没了) ,新版本的是每个链表分一个锁,分段锁是好几个链表共用同一个锁(锁冲突概率要比每个链表一把锁更高,代码实现起来更复杂了)
HashMap
key
允许为null
,HashTable
和ConcurrentHashMap
key
不能为null