线程安全的集合类

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 多线程环境使用 ArrayList
  • 多线程环境使用队列
  • 线程环境使用哈希表
    • Hashtable和 ConcurrentHashMap的对比.
      • 1.锁的粒度不同
      • 2.锁的优化
      • 3.扩容方式不同


前言

我会简单的介绍一些线程安全的集合类,还有它们的基本构成,大家请不要安心难度的问题,现在只是简单的了解一下.为什么要涉及线程安全的集合类呢?因为假如说我们要在多线程环境下使用,怎么办?这里提供了俩种方案.
1.最直接的办法,使用锁,手动保证~
多个线程去修改ArrayList 此时就可能有问题就可以给修改操作进行加锁~~
2.标准库还提供了一些线程安全的版本的集合类

多线程环境使用 ArrayList

在多线程环境中,使用 ArrayList 可能会引发线程安全问题。因为 ArrayList 并不是线程安全的容器,多个线程同时修改同一个 ArrayList 实例可能会导致数据不一致或者抛出异常。
如果非要使用的话,我提供了以下解决方案

  1. 自己使用同步机制 (synchronized 或者 ReentrantLock)
  2. Collections.synchronizedList(new ArrayList);

synchronizedList 是标准库提供的一个基于 synchronized 进行线程同步的 List.
synchronizedList 的关键操作上都带有 synchronized

  1. 使用 CopyOnWriteArrayList
    CopyOnWrite容器即写时复制的容器。

当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,
复制出一个新的容器,然后新的容器里添加元素,
添加完元素之后,再将原容器的引用指向新的容器。
这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会
添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

优点:
在读多写少的场景下, 性能很高, 不需要加锁竞争.
缺点:

  1. 占用内存较多.
  2. 新写的数据不能被第一时间读取到.

多线程环境使用队列

在多线程环境中,使用队列是非常常见的场景,但是需要注意队列的线程安全问题。以下是几种线程安全的队列:

ConcurrentLinkedQueue:这是一个线程安全的无界队列,内部使用单向链表实现,适合于高并发场景;
LinkedBlockingQueue:这是一个线程安全的有界队列,内部使用单向链表实现,适合于固定大小的任务队列;
ArrayBlockingQueue:这是一个线程安全的有界队列,内部使用数组实现,适合于固定大小的任务队列;
PriorityBlockingQueue:这是一个线程安全的优先级队列,内部使用堆实现,适合于按优先级排序的任务队列;
SynchronousQueue:这是一个线程安全的队列,没有任何内部容量,每个插入操作必须等待一个相应的删除操作,适合于生产者和消费者交替执行的场景。
以上队列都是线程安全的,能够在多线程环境中提供高效的并发操作。需要根据实际场景选择适合的队列类型,并根据具体需求设置容量、优先级等参数。

线程环境使用哈希表

ConcurrentHashMap:这是一个线程安全的哈希表,适合于高并发场景,内部采用分段锁机制,不同的段之间可以并发操作,能够提供更好的并发性能;

Hashtable:这是一个线程安全的哈希表,内部使用 synchronized 关键字来实现线程安全,但是并发性能不如 ConcurrentHashMap;

Collections.synchronizedMap():这是一个将非线程安全的哈希表转换为线程安全的哈希表的方法,内部使用 synchronized 关键字来实现线程安全,但是并发性能不如 ConcurrentHashMap。
以上哈希表都是线程安全的,能够在多线程环境中提供高效的并发操作。需要根据实际场景选择适合的哈希表类型,并根据具体需求设置容量、并发性能等参数。


当然我推荐使用ConcurrentHashMap,为什么呢?我们来看一下

Hashtable和 ConcurrentHashMap的对比.

1.锁的粒度不同

Hashtable
线程安全的集合类_第1张图片
ConcurrentHashMap
线程安全的集合类_第2张图片

2.锁的优化

ConcurrentHashMap,更充分的利用了CAS机制无锁编程.
有的操作,比如获取/更新元素个数,就可以直接使用CAS完成,不必加锁了
CAS也能保证线程安全,往往比锁更高效,但是这个东西咱们也不会特别经常使用.适用范围不像锁那么广泛

3.扩容方式不同

扩容触发条件:Hashtable 的扩容触发条件是当哈希表中元素个数大于等于阈值时,就需要进行扩容,阈值是容量 * 负载因子,负载因子默认为 0.75;ConcurrentHashMap 的扩容触发条件是当某个段的元素个数大于等于段容量的 75% 时,就需要对该段进行扩容。

扩容策略:HashTable的扩容需要重新申请内存空间,搬运元素(把元素从旧的哈希表上删掉,插入到新的哈希表上.)如果本身元素特别多,上亿个,搬运一次,成本就很高,就会导致这一次put操作就非常卡顿
ConcurrentHashMap的扩容策略就是,化整为零,并不会试图一次性就把所有的元素都搬运过去,而是每次搬运一部分.当put触发扩容,此时就会直接创建更大的内存空间,但不会直接把所有元素搬运过去,而是搬运一小部分.

你可能感兴趣的:(多线程,java)