目录
多线程下使用ArrayList
1.⾃⼰使⽤同步机制 (synchronized 或者 ReentrantLock)自行加锁(推荐)
2.使用Collections.synchronizedList(new ArrayList);
3.使⽤ CopyOnWriteArrayList
多线程使用哈希表
ConcurrentHashMap
ConcurrentHashMap核心优化点
多线程下使用ArrayList
1.⾃⼰使⽤同步机制 (synchronized 或者 ReentrantLock)自行加锁(推荐)
分析清楚,要把哪些代码打包到一起,成为一个“原子”操作
2.使用Collections.synchronizedList(new ArrayList);
返回list的各种关键方法都是带有synchronized
3.使⽤ CopyOnWriteArrayList
不去加锁,编程中的一些常见的思想方法
写时拷贝
修改不同变量/读取
多线程读取
确保读取过程中,要么读到的是旧版数据,要么读到的是新版数据
不会读到“修改”一半的数据
这个过程没有加锁,不会产生阻塞
但是很明显上述方案也有缺点
1.数组特别大,非常低效
2.如果多个线程同时修改也容易出问题
适合于特定场景的方案
服务器进行重新加载配置的时候
很多程序提供很多功能,按需开启/按需设置
服务器正在运行,但是需要修改配置
很多服务器也会提供配置重加载reload
配置就是被读取到服务器的内存中,以数组/哈希存储
服务器代码中的其他逻辑就会读取这些数组/哈希中的值,不会修改
此时,程序员手动修改配置文件后,手动触发reload功能
服务器就会创建新的数组/哈希,加载新的配置
加载完毕,使用新配置代替旧配置
多线程使用哈希表
HashMap线程不安全
Hashtable线程安全的(给各种public方法都加synchronized)
ConcurrentHashMap
效率更高,按照桶级别进行加锁,而不是给整个哈希加一个全局锁,有效降低锁冲突的概率
如果修改的两个元素,在不同链表上,本身就不涉及线程安全问题(修改不同变量)
如果修改同一个链表上的两个元素,可能就有线程安全问题,比如把这俩元素插入同一元素后面就可能产生竞争
针对不同的锁对象加锁,不会产生锁竞争(不会阻塞)
直接使用每个链表的头结点作为synchronized的锁对象
锁开销最大的地方就是阻塞
实际开发中用到的hash表可能是比较大的,桶有很多很多个
即使多线程访问上述的哈希表,同一时刻两个线程恰好访问同一个链表的可能性概率就比较第
ConcurrentHashMap核心优化点
1.把锁整个表=》锁头桶
2.使用原子类针对size进行维护
3.针对哈希扩容的场景
化整为零
确保每个操作的加锁时间不要太长
扩容操作意味着需要创建更大的数组
把旧哈希中的所有元素搬运到新的哈希中(元素很多,耗时很长)
一口气进行所有的搬运比较耗时
把整个搬运拆成多次来完成