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

来源:互联网 发布:费根鲍姆常数 知乎 编辑:程序博客网 时间:2024/05/16 15:07

再学习多线程编程的时候,看到如下代码。


package com.cbf4life;import java.util.*;public class ThreadSafeDemo {    public ThreadSafeDemo() {        ThreadGroup group=new ThreadGroup("testGroup");        MyThread at=new MyThread();        for(int i=0;i<10000;i++){            Thread th=new Thread(group,at,String.valueOf(i));            th.start();        }        while (group.activeCount() > 0) {            try {                Thread.sleep(10);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        System.out.println(at.list0.size());        System.out.println(at.list0.get(0));    }    public static void main(String[] args) {        new ThreadSafeDemo();    }    class MyThread implements Runnable {        List<String> list0=new ArrayList<String>(); //thread not safe//        Vector<String> list0=new Vector<String>(); //thread not safe//        List<String> list0=Collections.synchronizedList(new ArrayList<String>()); //thread safe        public void run() {            try {                Thread.sleep((int)(Math.random()*2));            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            list0.add(Thread.currentThread().getName());        }    }}

这段代码的原意是举例说明ArrayList是线程不安全的,然后在运行的时候可能会出现多种结果,

第一种结果

9976
0

大致原因是list的add方法导致的。

/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */public boolean add(E e) {    ensureCapacityInternal(size + 1);  // Increments modCount!!    elementData[size++] = e;    return true;}
这个是list的add方法,
elementData[size++] = e;

这一段代码块就是问题所在,size++这个过程可以分为三步

1.从内存中取到size的值。

2.计算size的值。

3.将size的值写到内存当中。

这不是一个原子性的操作,当多线程添加的情况下,假如size=0时,添加第一个元素的线程添加成功,同时在size没有及时写到内存中时,这时另一个线程继续添加就覆盖了原来的值导致最终list的值为9976。



第二种结果

9976
1

按道理说结果应该是9976 0,但是还是上面说的原因,原来的0被1覆盖了。


第三种结果

数组下标越界异常

原因可能出现在数组扩容的时候,假如当前数组的大小为8,而我添加第7个元素的时候,数组当前不需要扩容。然后这个线程进入阻塞,另一个线程也添加元素,发现当前数组大小还有一个容量,于是添加成功已经有8个元素,然后前一个阻塞线程开始添加元素,然后数组没有扩容,所以数组就容不下这个元素,于是数组下标越界了。


这个例子是在说明ArrayList的非线程安全的特性。