首页 > 其他分享 >list集合的线程安全问题分析

list集合的线程安全问题分析

时间:2023-11-25 18:03:39浏览次数:25  
标签:元素 elementData list minCapacity 线程 数组 集合 size

一、ArrayList

先说结论,ArrayList是线程不安全的。至于为什么需要去了解它的实现原理,来看下它的源码。

首先ArrayList是基于数据实现的,分析它的线程安全问题需要看下add方法

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //真正存储元素的数组
    transient Object[] elementData; // non-private to simplify nested class access
	//list当前的元素个数
    private int size;  
    
    //添加元素的方法
    public boolean add(E e) {
    	//这个方法确保elementData数组有足够的容量来存储元素e,也就是如果容量不够会进行扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //把元素e放入数组size下标处并让size自增,
        //在多线程环境下++操作不是原子的,所以会产生线程安全问题,所以多个线程同时给size自增时
        //最后得到的size是不正确的且结果不确定,那下次执行add时要往size处放元素,放的位置也就是不确定的
        //假设3个线程同时放,3次++操作得到的size可能都是1,最后三个线程都往1处放元素,相当于丢了2个元素
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
    	//先判断elementData是不是空,这个判断只有第一次add时才会进入
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        	// elementData是空需要计算出需要的最小容量,传进来的是size+1,DEFAULT_CAPACITY是10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        //如果最小容量比当前数组长度大就需要进行扩容
        if (minCapacity - elementData.length > 0)
            //扩容方法
            grow(minCapacity);
    }
    
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新容量=旧容量+旧容量的一半
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //复制数组元素
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //这个扩容的过程也是线程不安全的,假设线程t1计算出newCapacity后线程上下文切换了,其他线程又add了很多
    //元素这时容量可能已经变了,t1再恢复执行继续复制数组元素时就会出问题。
}

通过上边对源码的分析可以了解到ArrayList确实是线程不安全的。

二、线程安全的list实现

2.1 Vector

Vector 是一个线程安全的实现,它保证线程安全的方式就是每个方法都是用synchronized修饰的。

2.2 java.util.Collections#synchronizedList(java.util.List)

这个工具方法接收一个list,然后会对这个list进行包装,让每个方法都被synchronized修饰,

所以锁对象是当前List对象,读写不能同时进行。

2.3 CopyOnWriteArrayList

这个list实现内部有一个ReentrantLock,在写数据的时候先加锁,然后会把内部的数组复制一份进行写操作,通过这种方式保障线程安全,具体需要看下源码.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    /** 锁对象 */
    final transient ReentrantLock lock = new ReentrantLock();
    
    //存元素的数组
    private transient volatile Object[] array;
    
    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        //添加元素时先加锁
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            //复制旧数组的元素到新数组,容量+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //把元素e添加到新数组中
            newElements[len] = e;
            //把新数组赋值给存元素的数组arr
            setArray(newElements);
            return true;
        } finally {
        	//释放锁
            lock.unlock();
        }
    }
    //获取元素的方法不加锁,存的同时可以进行获取,性能高
    public E get(int index) {
        return get(getArray(), index);
    }
}

标签:元素,elementData,list,minCapacity,线程,数组,集合,size
From: https://www.cnblogs.com/chengxuxiaoyuan/p/17855815.html

相关文章

  • 多线程编程之——终止(打断)正在执行中的线程
    多线程编程之——终止(打断)正在执行中的线程ps:文字有点多,想看结果的,直接跳转:《二》一、基础知识1、我们基于spring开发,把线程都交给spring把线程交给spring管理好不好?将线程交给Spring管理是一个常见的做法,特别是在基于Spring的应用程序中。通过将线程纳入Spring的管理......
  • Probabilistic principal component analysis-based anomaly detection for structure
    SHMcanprovidealargeamountofdatathatcanrevealthevariationinthestructurecondition什么是压缩传感,数据重构,研究背景与意义,怎么用基于模型的方法不可避免的缺点是模型的不确定性,因为很难创建能够模拟真实物理情况的可靠的结构模型。为了克服基于模型的方法的缺......
  • Sumsets(UVA10125)整数集合
    备课的时候发现了这道题,对于初识哈希来说并不算一道很简单的题。在查阅林厚从老师的示例代码与往届OI选手的博客后,大致理解了本题的思路。相关标签:Hash跳转至本题Description给定一个整数集合S,求一个最大的d,满足a+b+c=d,其中a,b,c,d∈SInput多组数据,每组数据包括:第一行一......
  • winform 使用了invoke还是报错 线程间操作无效: 从不是创建控件“Form2”的线程访问它
    winform开发中,遇到“线程间操作无效:从不是创建控件“Form2”的线程访问它”,明明使用了网上说的this.invoke,怎么还是会报这个错误呢?代码如下,由于是测试configureAwait功能时发现的,所以带了它的一些使用 privateasyncvoidbutton7_Click(objectsender,EventArgse)//点......
  • 一个用于多线程共享数据保护测试的简易游戏服务器代码
    #include<iostream>#include<thread>#include<list>#include<mutex>//一个线程负责从客户端读取用户的命令,放入一个队列中;//另一个线程负责从队列中读取命令并解析,假设用一个int变量代表一个命令。classA{public://这里无法模拟从网络接受命令的过程,我们......
  • delphi 遍历集合类型
    遍历集合类型代码通过for-in循环遍历usesSystem.TypInfo;procedureTForm1.Button1Click(Sender:TObject);varvAnchors:TAnchors;vAnchor:TAnchorKind;beginvAnchors:=[akLeft,akTop,akBottom];forvAnchorinvAnchorsdobeginMemo1.Lines.......
  • 前端项目实战壹佰肆拾react-admin+material ui-react-admin之useListContext声明式版
    我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端技术群import{WithListContext}from'react-admin';import{Typography}from'@mui/material';exportconstAside=()=>(<WithListContextrender={({data,isLoading})=>......
  • 前端项目实战壹佰肆拾叁react-admin+material ui-react-admin之useList之filter
    我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端技术群const{data,total}=useList({data:[{id:1,name:'Arnold'},{id:2,name:'Sylvester'},{id:3,name:'Jean-Claude'},],filter:{n......
  • 前端项目实战壹佰肆拾贰react-admin+material ui-react-admin之useList之data
    我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端技术群constdata=[{id:1,name:'Arnold'},{id:2,name:'Sylvester'},{id:3,name:'Jean-Claude'},];必须是一个记录数组......
  • 前端项目实战壹佰肆拾肆react-admin+material ui-react-admin之useList之isFetching
    我是歌谣放弃很容易但是坚持一定很酷微信公众号关注前端小歌谣带你进入前端技术群import{useList,ListContextProvider,Datagrid,TextField,useGetList}from'react-admin';exportconstRUserListData=()=>{const{data,isFetching......