ArrayList线程不安全详解

原创 2015年07月08日 15:33:19

首先需要了解什么是线程安全:线程安全就是说多线程访问同一代码(对象、变量等),不会产生不确定的结果。 既然说ArrayList是线程不安全的,那么在多线程中操作一个ArrayList对象,则会出现不确定的结果。具体是怎样不确定,请看测试下面这段代码(在此测试ArrayList的add方法):

public class ArrayListInThread implements Runnable{

    //线程不安全
    private List threadList = new ArrayList();
    //线程安全
    //private List threadList = Collections.synchronizedList(new ArrayList());

    @Override
    public void run() {
        try {
            Thread.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //把当前线程名称加入list中
        threadList.add(Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException{
        ArrayListInThread listThread = new ArrayListInThread();

        
        for(int i = 0; i < 100; i++){
            Thread thread = new Thread(listThread, String.valueOf(i));
            thread.start();
        }
        
        //等待子线程执行完
        Thread.sleep(2000);

        System.out.println(listThread.threadList.size());
        //输出list中的值
        for(int i = 0; i < listThread.threadList.size(); i++){
            if(listThread.threadList.get(i) == null){
                System.out.println();;
            }
            System.out.print(listThread.threadList.get(i) + "  ");
        }
    }
}
执行几次会发现结果不一样,甚至有时会出现ArrayIndexOutOfBoundsException,贴出几次执行的结果:

结果一:

结果二:

结果三:

以上执行结果说明ArrayList确实是线程不安全的,然后我们从线程并发的角度分析,为何会出现这样的结果:

首先,我们先来看一下ArrayList中的add方法是如何实现的(查看详细源码请点击):

//添加元素e    
    public boolean add(E e) {    
        // 确定ArrayList的容量大小    
        ensureCapacity(size + 1);  // Increments modCount!!    
        // 添加e到ArrayList中    
        elementData[size++] = e;    
        return true;    
    }    
    
    // 确定ArrarList的容量。    
    // 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”    
    public void ensureCapacity(int minCapacity) {    
        // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的    
        modCount++;    
        int oldCapacity = elementData.length;    
        // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1”    
        if (minCapacity > oldCapacity) {    
            Object oldData[] = elementData;    
            int newCapacity = (oldCapacity * 3)/2 + 1;    
            //如果还不够,则直接将minCapacity设置为当前容量  
            if (newCapacity < minCapacity)    
                newCapacity = minCapacity;    
            elementData = Arrays.copyOf(elementData, newCapacity);    
        }    
    }

结果中,有的值没有出现(结果一中3没有出现),有的出现了null值,这是由于赋值时出现了覆盖。赋值语句为:elementData[size++] = e,这条语句可拆分为两条:
1. elementData[size] = e;
2. size ++;
假设A线程执行完第一条语句时,CPU暂停执行A线程转而去执行B线程,此时ArrayList的size并没有加一,这时在ArrayList中B线程就会覆盖掉A线程赋的值,而此时,A线程和B线程先后执行size++,便会出现值为null的情况;至于结果三中出现的ArrayIndexOutOfBoundsException异常,
则是A线程在执行ensureCapacity(size+1)后没有继续执行,此时恰好minCapacity等于oldCapacity,B线程再去执行,同样由于minCapacity等于oldCapacity,ArrayList并没有增加长度,B线程可以继续执行赋值(elementData[size] = e)并size ++也执行了,此时,CPU又去执行A线程的赋值操作,由于size值加了1,size值大于了ArrayList的最大长度,
因此便出现了ArrayIndexOutOfBoundsException异常。

既然ArrayList是线程不安全的,但如果需要在多线程中使用,可以采用list<Object> list =Collections.synchronizedList(new ArrayList<Object>)来创建一个ArrayList对象。

版权声明:本文为原创文章,转载请注明出处。

ArrayList线程不安全分析

分析ArrayList的线程不安全因素,不对之处欢迎大家指正

为什么说ArrayList是线程不安全的?

首先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。...

ArrayList的实现原理以及实现线程安全

一、 ArrayList概述:   ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。       ArrayList...
  • yztezhl
  • yztezhl
  • 2016年09月08日 10:06
  • 4182

解决ArrayList线程不安全

前些天做项目时,程序出现意外的问题,经后来分析是使用ArrayList这个线程不安全的方法导致 解决这个问题通常有两种方法(个人认为) 一:使用synchronized关键字,这个大家应该都很熟悉...
  • sdefzhpk
  • sdefzhpk
  • 2012年03月01日 19:29
  • 25227

为什么说ArrayList是线程不安全的?

一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成: 1. 在 Items[Size] 的位置存放此元素; 2. 增大 Size 的值。   在单线程运行的情况下,如果 Si...
  • varyall
  • varyall
  • 2016年12月18日 23:39
  • 747

ArrayList在添加元素的时候会出现的一些线程不安全的现象

再学习多线程编程的时候,看到如下代码。 package com.cbf4life; import java.util.*; public class ThreadSafeDemo { ...

jdbc事务回滚 批量操作数据 一个例子

一个例子
  • zygzzp
  • zygzzp
  • 2015年02月11日 16:01
  • 2031

MongoDB经典面试题集锦

1.什么是NoSQL数据库?NoSQL和RDBMS有什么区别?在哪些情况下使用和不使用NoSQL数据库?  NoSQL是非关系型数据库,NoSQL = Not Only SQL。  关系型数据库采用的...

解决ArrayList线程不安全

解决ArrayList线程不安全
  • educast
  • educast
  • 2016年08月09日 11:31
  • 569

HashMap为什么是线程不安全的?

一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?HashMap底层是一个Entry数组,当发生hash冲突的时候,has...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ArrayList线程不安全详解
举报原因:
原因补充:

(最多只允许输入30个字)