多线程:线程安全容器
1.List
1.Vector
Vector线程安全:在写操作时,加上了synchronized关键字保证线程安全,在一些对线程安全要求较高,且写入操作相对较少,读取操作较多的场景下可以使用。
2.使用Collections.synchronizedList包装
Collections.synchronizedList
是一个工具方法,它可以将一个普通的List
包装成一个线程安全的列表。它内部也是通过同步机制来实现线程安全的,在调用List
的方法时,会对整个列表进行同步操作。
3.CopyOnWriteArrayList
实现原理是在进行写入操作(如添加、修改、删除元素)时,会先复制一份当前的数组,然后在新的数组上进行操作,操作完成后再将新数组赋值给原数组。这样可以保证在写入操作时,不会影响到其他线程对原数组的读取操作,适用于读多写少的并发场景。
4.ConcurrentLinkedList
采用了乐观锁和 CAS(Compare and Swap)算法来实现高效的并发操作,不需要对整个列表进行加锁,从而提高了并发性能,比Vector
和synchronizedList
更高的性能,尤其是在插入和删除操作频繁的情况下。
总结
类型 | Vector | Collections.synchronizedList | CopyOnWriteArrayList | ConcurrentLinkedList |
---|---|---|---|---|
线程安全机制 | synchronized 关键字 | 对整个列表同步 | 读写分离,复制数组 | 乐观锁和 CAS 算法 |
性能特点 | 写入操作开销大 | 可能存在性能问题 | 读操作性能高,写入开销大 | 高并发下性能好 |
适用场景 | 线程安全要求高,写入少读取多 | 已有List 需在多线程中使用 | 读多写少的场景 | 高并发的插入和删除场景 |
2.Map
1.hashtable
通过synchronized
关键字实现了对内部方法的同步访问,Hashtable不允许使用
null作为键或值,否则会抛出
NullPointerException,在一些对线程安全要求较高,且写入操作相对较少,读取操作较多的场景下可以使用。
2.使用Collections.synchronizedMap包装
可以将任何实现了Map
接口的对象进行包装,使其变为线程安全的映射,具有较高的灵活性。
3.ConcurrentHashMap
JDK 8 及以后的版本中采用了 CAS 算法和Synchronized
关键字来实现线程安全,适用于高并发的场景,尤其是在读写操作都比较频繁的情况下。
4.ConcurrentSkipListMap(跳表)
ConcurrentSkipListMap
中的元素是按照键的自然顺序或指定的比较器进行排序的,在高并发环境下,插入、删除和查找操作的性能都比较稳定,不会出现像哈希表那样在扩容时可能导致的性能抖动,ConcurrentSkipListMap不允许使用
null作为键,否则会抛出
NullPointerException。
总结
类型 | Hashtable | Collections.synchronizedMap | ConcurrentHashMap | ConcurrentSkipListMap |
---|---|---|---|---|
线程安全机制 | synchronized 关键字 | 对整个映射同步 | 分段锁(JDK 8 及以后采用 CAS 和Synchronized ) | 跳表数据结构和同步机制 |
性能特点 | 写入操作开销大 | 可能存在性能问题 | 高并发下读写性能好 | 高并发下性能稳定,有序遍历高效 |
键值是否允许为空 | 键和值都不允许为null | 取决于原始映射 | JDK 8 及以后允许值为null ,键不允许 | 键不允许为null ,值可以为null |
适用场景 | 线程安全要求高,写入少读取多 | 已有Map 需在多线程中使用 | 高并发的读写场景 | 需要有序存储和高并发访问的场景 |
3.Set
1.使用Collections.synchronizedSet包装
通过Collections.synchronizedSet
方法对普通的Set
进行包装,在内部对Set
的所有操作都进行同步处理,确保在多线程环境下,同一时刻只有一个线程能够访问Set
的方法,从而实现线程安全。
2.CopyOnWriteArraySet
CopyOnWriteArraySet
内部基于CopyOnWriteArrayList
实现。在进行写入操作(如添加、删除元素)时,它会首先复制一份当前的数组,然后在新的数组上进行操作,操作完成后再将原数组的引用指向新数组。读取操作则直接在原数组上进行,不需要加锁,从而实现了读写分离,保证了线程安全,适合于读多写少的场景。
3.ConcurrentSkipListSet
ConcurrentSkipListSet
内部基于跳表数据结构实现。通过在不同层次上建立索引,使得查找、插入和删除操作都能在对数时间复杂度内完成,适用于在多线程环境中使用一个有序的、线程安全的Set
,并且对并发性能有较高要求的场景。
总结
类型 | Collections.synchronizedSet | CopyOnWriteArraySet | ConcurrentSkipListSet |
---|---|---|---|
实现原理 | 对普通Set 包装,同步所有操作 | 基于CopyOnWriteArrayList ,读写分离 | 基于跳表数据结构和同步机制 |
性能特点 | 并发度高时性能开销大 | 读多写少场景性能好 | 高并发下性能稳定,有序操作高效 |
数据一致性 | 强一致性 | 最终一致性 | 强一致性 |
内存开销 | 无额外开销 | 写入可能产生较大内存开销 | 相对较小 |
是否有序 | 取决于原始Set | 否 | 是 |
适用场景 | 将现有Set 用于多线程,性能要求不高 | 读多写少,允许最终一致性 | 多线程下需要有序且高性能的Set |