使用ArrayList()的多线程获得了ArrayIndexOutOfBoundsException

RaM*_*abU 3 java concurrency multithreading arraylist

我已经通过非线程安全对象(这里是ArrayList)运行以下代码来运行多个线程:

import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;

public class A implements Runnable {
    String name;

    static  List<Integer> list = new ArrayList();
    private static Object lock = new Object();

    A(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for(int i = 1; i <= 1000; i++) {
            list.add(i);
        }
        System.out.println(list.size());
    }
}
Run Code Online (Sandbox Code Playgroud)

我期待这段代码只是为了产生错误的答案,因为ArrayList它不是线程安全的.但相反,我得到这个错误:

Exception in thread "Thread-1" 1003
2401
2799
3799
java.lang.ArrayIndexOutOfBoundsException: 109
    at java.util.ArrayList.add(Unknown Source)
    at threads.A.run(A.java:16)5123

    at java.lang.Thread.run(Unknown Source)
Exception in thread "Thread-5" java.lang.ArrayIndexOutOfBoundsException: 4164
    at java.util.ArrayList.add(Unknown Source)
    at threads.A.run(A.java:16)
    at java.lang.Thread.run(Unknown Source)
6123
Run Code Online (Sandbox Code Playgroud)

任何人都可以向我解释导致这个特定错误的原因是什么?

Era*_*ran 8

好吧,您在多线程环境中使用非线程安全的集合而没有任何同步.

让我们检查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;
}
Run Code Online (Sandbox Code Playgroud)

当多个线程同时调用此方法时,很可能会ensureCapacityInternal(size + 1)验证是否有足够的空间容纳1个新元素,但是多个线程会尝试同时添加一个元素,因此elementData[size++]会抛出ArrayIndexOutOfBoundsException一些元素.


And*_*lko 7

ArrayList 不是一个线程安全的类.

元素的底层存储Object[]是一个数组.任何数组都需要在编译时预定义的内存中的分配空间.但是,当ArrayList"想要"添加新元素(超出底层数组绑定)时,必须完成几件事(不知情).底层数组获得新的(增加的)长度.旧的数组的每个元素被复制到新的数组,然后加入新的元素.因此,在多线程环境中使用a 时,可以预期ArrayIndexOutOfBoundsException异常ArrayList.

您添加的元素太快,因此ArrayList#add() -> grow() -> newCapacity()无法计算为所有元素分配内存的正确容量.

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}
Run Code Online (Sandbox Code Playgroud)

在某些时候,s == elementData.length内部条件ArrayList#add表示有一个新元素的空间A.紧接着其他线程将其元素放入列表中.现在不存在用于空间AelementData[s] = e;抛出异常.