线程安全的List

article/2025/8/24 13:10:12

线程安全的List

    • Vector
      • 类的架构
      • 基本属性
      • 构造方法
      • 基本方法
    • SynchronizedList和SynchronizedRandomAccessList
      • Collections.synchronizedList
      • 构造方法
      • 具体方法
      • 具体使用
    • CopyOnWriteArrayList(**)
      • 简介
      • 结构
      • 成员变量
      • 常见方法
        • add (***)
        • remove
        • get
      • CopyOnWriteArrayList总结
    • 总结

在我们开发的时候用的最多的就是无序的ArrayList 和 有序的LinkedList,但这两个List都是线程不安全的;那如果我们需要一款线程安全的List该如何选择呢

Vector

首先是Vector ,这是一个比较老旧的集合类,它与ArrayList 非常相似,都是基于数组实现。

类的架构

public class Vector<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Vector
由于实现了RandomAccess接口,所以使用for循环可以更快捷的遍历。
很多与ArrayList类似的地方可以参考我的另一篇文章- ArrayList解析

基本属性

// 底层存储 由数组实现
protected Object[] elementData;
// 实际存储大小
protected int elementCount;
// 扩容参数** 在扩容时会根据这个参数(不为0)去扩容大小
protected int capacityIncrement;private static final long serialVersionUID = -2767605614048989439L;

这里需要注意capacityIncrement这个参数是ArrayList中没有的,用于扩容。

构造方法

public Vector() {// 默认容量为10this(10);
}
public Vector(int initialCapacity) {// 默认扩容因子为0this(initialCapacity, 0);
}
// 初始容量 扩容因子
public Vector(int initialCapacity, int capacityIncrement) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);this.elementData = new Object[initialCapacity];this.capacityIncrement = capacityIncrement;
}
public Vector(Collection<? extends E> c) {elementData = c.toArray();elementCount = elementData.length;// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

基本方法

它几乎所有的方法都加上了 synchronized 关键词来实现线程安全,这里特别关注下 扩容 算法

public synchronized void copyInto(Object[] anArray) {System.arraycopy(elementData, 0, anArray, 0, elementCount);
}public synchronized void trimToSize() {modCount++;int oldCapacity = elementData.length;if (elementCount < oldCapacity) {elementData = Arrays.copyOf(elementData, elementCount);}
}public synchronized void ensureCapacity(int minCapacity) {if (minCapacity > 0) {modCount++;ensureCapacityHelper(minCapacity);}
}private void ensureCapacityHelper(int minCapacity) {// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 扩容 ***
private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// 如果扩容因子(capacityIncrement) 大于0,那么 新长度 = 旧长度 + 扩容因子// 否则 新长度 = 2 * 旧长度// 扩容算法是与ArrayList不同的点int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);
}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}public synchronized void setSize(int newSize) {modCount++;if (newSize > elementCount) {ensureCapacityHelper(newSize);} else {for (int i = newSize ; i < elementCount ; i++) {elementData[i] = null;}}elementCount = newSize;
}

大致上所有的方法都与ArrayList一致,不过都加上了synchronized关键字,这里不过多累述;具体方法可以查看我另一篇博客介绍 - ArrayList源码解析 。

SynchronizedList和SynchronizedRandomAccessList

它比Vector更为强大,它可以把所有的List接口实现类(包括ArrayList LinkedList等),都转为线程安全的list。

他有两种类型,都为Collections静态内部类

  • SynchronizedList : (LinkedList等)
  • SynchronizedRandomAccessList : 实现了RandomAccess的list(ArrayList,Vector等)

Collections.synchronizedList

public static <T> List<T> synchronizedList(List<T> list) {// 判断list是否实现了 RandomAccess 接口(ArrayList实现了,LinkedList未实现)return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list) :new SynchronizedList<>(list));
}
// mutex 对象锁
static <T> List<T> synchronizedList(List<T> list, Object mutex) {return (list instanceof RandomAccess ?new SynchronizedRandomAccessList<>(list, mutex) :new SynchronizedList<>(list, mutex));
}
static class SynchronizedRandomAccessList<E>extends SynchronizedList<E>implements RandomAccessstatic class SynchronizedList<E>extends SynchronizedCollection<E>implements List<E> 

构造方法

SynchronizedList(List<E> list) {super(list);this.list = list;
}
// mutex 为同步锁
SynchronizedList(List<E> list, Object mutex) {super(list, mutex);this.list = list;
}

具体方法

static class SynchronizedList<E>extends SynchronizedCollection<E>implements List<E> {private static final long serialVersionUID = -7754090372962971524L;final List<E> list;SynchronizedList(List<E> list) {super(list);this.list = list;}SynchronizedList(List<E> list, Object mutex) {super(list, mutex);this.list = list;}public boolean equals(Object o) {if (this == o)return true;synchronized (mutex) {return list.equals(o);}}public int hashCode() {synchronized (mutex) {return list.hashCode();}}public E get(int index) {synchronized (mutex) {return list.get(index);}}public E set(int index, E element) {synchronized (mutex) {return list.set(index, element);}}public void add(int index, E element) {synchronized (mutex) {list.add(index, element);}}public E remove(int index) {synchronized (mutex) {return list.remove(index);}}public int indexOf(Object o) {synchronized (mutex) {return list.indexOf(o);}}public int lastIndexOf(Object o) {synchronized (mutex) {return list.lastIndexOf(o);}}public boolean addAll(int index, Collection<? extends E> c) {synchronized (mutex) {return list.addAll(index, c);}}public ListIterator<E> listIterator() {return list.listIterator(); // Must be manually synched by user}public ListIterator<E> listIterator(int index) {return list.listIterator(index); // Must be manually synched by user}public List<E> subList(int fromIndex, int toIndex) {synchronized (mutex) {return new SynchronizedList<>(list.subList(fromIndex, toIndex),mutex);}}@Overridepublic void replaceAll(UnaryOperator<E> operator) {synchronized (mutex) {list.replaceAll(operator);}}@Overridepublic void sort(Comparator<? super E> c) {synchronized (mutex) {list.sort(c);}}private Object readResolve() {return (list instanceof RandomAccess? new SynchronizedRandomAccessList<>(list): this);}
}

可以看到他几乎对所有的方法都加上了synchronized (mutex)同步对象锁,以此来实现list线程安全。

所以他不是性能的最优选,至少在读多写少的情况下,性能非常糟糕。

具体使用

ArrayList<String> arrayList = new ArrayList();
// 通过Collections静态方法,把list转为线程安全的list
List syncList = Collections.synchronizedList(arrayList);

CopyOnWriteArrayList(**)

简介

从字面意思上来看 基于复制在写的ArrayList ,也就是在写的操作时把原数组复制一份在添加新元素。
支持高效率且是线程安全的,读操作无锁,写操作有锁(基于底层复制)
适合读多写少的场景。
他还有个兄弟叫 CopyOnWriteArraySet,这里不涉及set,所以不展开。

结构

public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList

成员变量

private static final long serialVersionUID = 8673264195747942595L;
// 将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化
final transient ReentrantLock lock = new ReentrantLock();
// volatile:线程对volatile变量的修改会立刻被其他线程所感知,即不会出现数据脏读的现象,从而保证数据的“可见性”
private transient volatile Object[] array;

常见方法

add (***)

public boolean add(E e) {// 首先加锁final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;// 核心:复制底层数组,且长度+1Object[] newElements = Arrays.copyOf(elements, len + 1);// 在数组末尾添加元素newElements[len] = e;// 覆盖原数组对象setArray(newElements);return true;} finally {// 释放锁lock.unlock();}
}public void add(int index, E element) {// 先加锁final ReentrantLock lock = this.lock;lock.lock();try {// 获取原数组Object[] elements = getArray();int len = elements.length;if (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);Object[] newElements;int numMoved = len - index;// 在末尾添加if (numMoved == 0)newElements = Arrays.copyOf(elements, len + 1);else {// 新建数组 并使得长度+1newElements = new Object[len + 1];// 赋值给新数组 [原数组,起始索引,新数组,起始索引,长度]System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index, newElements, index + 1, numMoved);}newElements[index] = element;setArray(newElements);} finally {// 释放锁lock.unlock();}
}// 把c中存在 但原数组中不存在的值 添加到原数组中
public int addAllAbsent(Collection<? extends E> c) {Object[] cs = c.toArray();if (cs.length == 0)return 0;final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;int added = 0;// uniquify and compact elements in csfor (int i = 0; i < cs.length; ++i) {Object e = cs[i];if (indexOf(e, elements, 0, len) < 0 &&indexOf(e, cs, 0, added) < 0)cs[added++] = e;}if (added > 0) {Object[] newElements = Arrays.copyOf(elements, len + added);System.arraycopy(cs, 0, newElements, len, added);setArray(newElements);}return added;} finally {lock.unlock();}
}final void setArray(Object[] a) {array = a;
}

remove

public E remove(int index) {// 加锁final ReentrantLock lock = this.lock;lock.lock();try {// 获取原数组和原数组长度Object[] elements = getArray();int len = elements.length;E oldValue = get(elements, index);int numMoved = len - index - 1;if (numMoved == 0)// 移除末尾元素setArray(Arrays.copyOf(elements, len - 1));else {// 创建新数组,并赋值Object[] newElements = new Object[len - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index, numMoved);setArray(newElements);}// 返回旧值return oldValue;} finally {lock.unlock();}
}public boolean remove(Object o) {// 快照Object[] snapshot = getArray();// 获取o所在的索引int index = indexOf(o, snapshot, 0, snapshot.length);return (index < 0) ? false : remove(o, snapshot, index);
}// 获取o所在的索引
// [查询对象o,数组,起始索引,数组长度]
private static int indexOf(Object o, Object[] elements, int index, int fence) {if (o == null) {for (int i = index; i < fence; i++)if (elements[i] == null)return i;} else {for (int i = index; i < fence; i++)if (o.equals(elements[i]))return i;}return -1;
}

remove 和 add 等方法都加上了锁

get

private E get(Object[] a, int index) {return (E) a[index];
}
public E get(int index) {return get(getArray(), index);
}
final Object[] getArray() {return array;
}

很显然 get 方法并没有加锁

CopyOnWriteArrayList总结

  • 非常适合多读少写的场景,线程安全。
  • 数据一致性的问题,CopyOnWrite容器只能保证最终的数据一致性,并不能保证数据的实时性,也就是不具备原子性的效果
  • 数据修改,随着数组的元素越来越多,修改的时候拷贝数组将会越来越耗时。

总结

  • ArrayList 线程不安全
  • Vector是比较古老的线程安全的,几乎给所有方法都是synchronized,但性能不行。
  • synchronizedList 可以把所有的List的实现类转成线程安全的集合,内部也是用synchronized关键词
  • CopyOnWriteArrayList在兼顾了线程安全的同时,又提高了并发性,性能比Vector要高

http://chatgpt.dhexx.cn/article/3qx4OBo0.shtml

相关文章

Git - 拉取远程分支并创建本地分支

一、查看远程分支 使用如下git命令查看所有远程分支 git branch -r 查看远程和本地所有分支 git branch -a 查看本地分支 git branch 在输出结果中&#xff0c;前面带* 的是当前分支 二、拉取远程分支并创建本地分支 方法一 使用如下命令 git checkout -b 本地分支名…

Git获取远程分支文件并创建自己的远程分支

Git获取远程分支文件并创建自己的远程分支 1、前期准备软件&#xff1a;git-bash 2、获取远程分支文件过程 创建一个空的文件夹&#xff0c;选择文件夹&#xff0c;并右击&#xff0c;选择该选项打开命令行。 复制需要拉取的远程仓库地址。 &#xff08;2&#xff09;把maste…

git基于远程分支创建本地分支

git基于远程分支创建本地分支 1.首先 git branch -a 查看所有的分支 2.使用 git checkout -b 本地分支名 远程分支名 https://blog.csdn.net/north1989/article/details/116299912?utm_mediumdistribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm1001.…

git 创建本地分支及远程分支并且关联分支

git命令在创建本地分支及远程分支并且关联远程分支 为了便于版本的维护及管理将会不断的在master分支上创建出新的分支 大致分为: 首先切换到在要开的分支上――――>创建本地分支――――>创建远程分支――――>切换到本地分支――――>将本地分支与远程分支关联…

git 创建远程分支,并提交代码到该分支的操作

1. 首先&#xff0c;在本地创建这个分支 使用命令 git checkout -b 分支名 &#xff08;表示创建这个分支&#xff0c;并且切换到该分支&#xff09; 2. 创建远程分支 使用命令 git push --set-upstream origin 分支名 &#xff08;表示将分支推送到远程仓库&#xff09; 3. …

git创建远程分支并关联本地分支

场景一&#xff1a; 本地、远程都没有分支 "v1.0.0" 1. 先查看确认一下&#xff0c;命令&#xff1a; git branch -a 2. 创建本地分支&#xff0c;命令&#xff1a; git checkout -b v1.0.0 3. 创建远程分支&#xff0c;并且本地分支关联远程分支&#xff0c;命令…

Git 创建远程分支并提交代码到远程分支

1、可以通过git branch -r 命令查看远端库的分支情况 2、从已有的分支创建新的分支(如从master分支),创建一个dev分支 但此时并没有在远程仓库上创建分支 如图所示 还是只有一个master分支 3、建立本地到远端仓库的链接 --这样代码才能提交上去 使用命令行 git push --set-…

Git创建远程分支并提交代码到远程分支

1、可以通过git branch -r 命令查看远端库的分支情况 动图演示&#xff08;选择项目右键选择 Git Bash Here&#xff0c;然后输入命令git branch -r&#xff09;&#xff1a; 2、从已有的分支创建新的分支(如从master分支),创建一个dev分支 但此时并没有在远程仓库上创建分支 如…

机器学习知识经验分享之三:基于卷积神经网络的经典目标检测算法

文章目录 前言一、一阶段目标检测算法1.YOLO系列算法2.SSD检测算法3. RetinaNet检测算法 二、两阶段目标检测算法1.Faster R-CNN检测算法2.Mask R-CNN检测算法3.Cascade R-CNN检测算法 总结 前言 本系列文章将对机器学习知识进行分享总结。便于大家从理论层面了解人工智能基础…

轻量型目标检测算法一次看个够

序言 不知道大家有没有发现&#xff0c;近两年目标检测算法发展非常的快&#xff0c;恍惚一看&#xff0c;单阶段算法几乎统一了目标检测&#xff0c;各种高性能的目标检测算法层出不穷&#xff0c;印象中是在YOLOv4出来后&#xff0c;基于YOLO的改进变得一发不可收拾&#xf…

万字长文概述单目3D目标检测算法

一&#xff0c;理论基础-相机与图像 相机将三维世界中的坐标点&#xff08;单位为米&#xff09;映射到二维图像平面&#xff08;单位为像素&#xff09;的过程能够用一个几何模型进行描述&#xff0c;这个模型有很多种&#xff0c;其中最简单的称为针孔相机模型。相机的成像过…

yolov5 目标检测算法

简介&#xff1a; 目标检测在生活中应用领域非常广泛&#xff0c;列如&#xff1a;道路违规抓拍、未戴口罩识别、工地未佩戴安全帽抓拍、厨房出现老鼠检测。 还可以用在游戏辅助外挂。以枪战为例&#xff0c;在游戏过程中时刻检测有没有人头出现。当检测到目标人头&#xff0c;…

【快速入门】YOLOv5目标检测算法

文章目录 一、YOLOv5简介二、网络结构1、Input2、Backbone3、Neck4、Head 三、改进方法1、自适应锚框计算2、自适应灰度填充 四、性能表现五、YOLOv5入门实战 一、YOLOv5简介 YOLOv5是一个在COCO数据集上预训练的物体检测架构和模型系列&#xff0c;它代表了Ultralytics对未来…

目标检测算法汇集介绍

目标检测算法 目标检测概念 目标检测这里阐述两个应用场景&#xff0c;1 为物体位置检测&#xff0c;2 为物体关键点检测。 1 物体位置检测 相比与图片分类&#xff0c;目标检测算法结果要求不仅识别出图片中的物理类别并且输出物体的位置参数。 物体的位置通过bounding bo…

YOLOv3目标检测算法——通俗易懂的解析

目录 YOLOv3目标检测算法前沿一.YOLOv3二.损失函数 YOLOv3目标检测算法 前沿 前两篇文章我们讲了下关于YOLOv1和YOLOv2的原理&#xff0c;有不懂的小伙伴可以回到前面再看看&#xff1a; YOLOv1目标检测算法——通俗易懂的解析YOLOv2目标检测算法——通俗易懂的解析 作者出于…

单阶段目标检测算法之YOLOv1详解

官方网站C语言版本:https://pjreddie.com/darknet/yolov1/ tensorflow版本的代码下载&#xff1a; https://github.com/hizhangp/yolo_tensorflow 论文&#xff1a; http://arxiv.org/abs/1506.02640 目录 一、YOLO介绍 二、YOLOv1的结构 三、YOLOV1原理 &#xff08;一…

yolo-目标检测算法简介

一 简单概念 机器视觉的四大任务 分类-Classification&#xff1a;解决“是什么&#xff1f;”的问题&#xff0c;即给定一张图片或一段视频判断里面包含什么类别的目标. 检测-Detection&#xff1a;解决“是什么&#xff1f;在哪里&#xff1f;”的问题&#xff0c;即定位出…

目标检测算法发展历程

这里的图片是从b站同济子豪兄的【精读AI论文】 R-CNN深度学习目标检测算法 处截图的&#xff0c; 在这算是个记录吧&#xff0c;同时立个小小的flag&#xff0c;尽量在硕一结束前都搞明白是干啥的吧&#xff08;希望能坚持住 &#xff1a;&#xff09; 在这也小推一下子豪兄&a…

YOLOv5目标检测算法——通俗易懂的解析

目录 YOLOv5目标检测算法前沿一.网络结构1.1.Backbone1.2.Neck1.3.Head二.数据增强2.1.Mosaic2.2.Copy paste2.3.Random affine2.4.Mixup2.5.Albumentation2.6.Augment HSV2.7.Random horizontal flip 三.训练策略3.1.Multi-scale training3.2.Auto anchor3.3.Warmup and Cosin…

目标检测算法(YOLOv3)

目标检测算法(YOLOv3) YOLOv3在YOLOv2的基础上,改良了网络的主干,利用多尺度特征图进行检测,改进了多个独立的Logistic regression分类器来取代softmax来预测类别分类. 论文题目&#xff1a;YOLOv3: An Incremental Improvement 主干网络 YOLOv3提出了新的主干网络: Darknet…