有如下代码,stream只是简单的将数字按升序添加到列表中:
List<Integer> integers = new ArrayList<>();
ListThread listThread1 = new ListThread(integers);
ListThread listThread2 = new ListThread(integers);
ListThread listThread3 = new ListThread(integers);
ListThread listThread4 = new ListThread(integers);
ListThread listThread5 = new ListThread(integers);
ListThread listThread6 = new ListThread(integers);
ListThread listThread7 = new ListThread(integers);
listThread1.start();
listThread2.start();
listThread3.start();
listThread4.start();
listThread5.start();
listThread6.start();
listThread7.start();
listThread1.join();
listThread2.join();
listThread3.join();
listThread4.join();
listThread5.join();
listThread6.join();
listThread7.join();
System.out.println(integers);
显示以下错误:
Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Index 171 out of bounds for length 163
at java.base/java.util.ArrayList.add(ArrayList.java:455)
at java.base/java.util.ArrayList.add(ArrayList.java:467)
问题:据我了解,每个线程都有自己的缓存,每个线程在缓存中都有自己的字段版本integers,列表本身integers存储在 RAM 中。那么这种来自缓存的信息和来自RAM的信息的交互是如何发生的,因为如果一个线程想把一些元素放在数组之外,那么这个数组已经在这个线程的缓存中增加了。如果缓存中有很多变体,那么我们的 RAM 中的列表是如何形成的,哪个变体进入 RAM?
public class ListThread extends Thread {
private final List<Integer> list;
public ListThread(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
for (int i = 0; i < 400; i++) {
list.add(i);
}
}
}
这里的要点是线程在工作时相互竞争,而
ArrayList另一个答案中正确指出的 collection 不是线程安全的。这是您用来添加元素的add()类方法的样子:ArrayList这就是
add()它所依赖的私有方法的样子:线程同时执行公共方法调用
add(),这样做会产生各种“意想不到”的效果。可能会或可能不会抛出异常,但并非所有元素都会出现在最终列表中。让我们看一下异常。所有线程同时调用
add(),后者又调用 privateadd(),其中不执行块if(首先size小于数组的大小),添加元素并size递增变量。可能会发生所有线程同时增长的情况size,以至于它会跳过elementData.length,超过数组的实际大小。并且下一次调用私有方法时,add()块if仍然不会执行,数组不会扩展,我们将尝试在大于数组大小的索引处添加一个元素,因此异常。但是还有一种情况就是上面提到的。
add()同时调用而不相互等待的线程通常会用相同的调用它size,因为并非所有add()-s 都起作用,因此不会增加所需的次数。如果程序运行并且没有抛出异常,我们从这里得到相同位置的添加和“劣等”最终列表。ArrayList只是不是线程安全的对象。使用线程安全集合来处理来自多个线程的列表,例如英文 CO 中有选项。例如,您可以使用Collections.synchronizedList。