gpt4 book ai didi

java - ArrayList#contains 上的 ArrayIndexOutOfBounds(多线程)

转载 作者:行者123 更新时间:2023-11-30 02:07:08 25 4
gpt4 key购买 nike

我最近遇到了一个意外错误(代码不是我的),导致 ArrayIndexOutOfBoundsExceptionArrayList#contains 。相关代码如下。

private static final List<String> list = new ArrayList<>();

static void register() {
update();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
update();
}
}, 0, 21600000);
}

private static void update() {
list.clear();
new Thread(() -> {
List<String> other; //should always be the same length.
list.addAll(other);
}).start();
}

public static boolean contains(String string) { //called long after register
return list.contains(string); //throws ArrayIndexOutOfBounds
}

我很清楚ArrayList不是线程安全的,可以用 Collections#synchronizedList 之类的东西来修复它。我的问题是了解这个特定的代码如何抛出 ArrayIndexOutOfBoundsException .

异常的堆栈跟踪在 ArrayList#indexOf 中标识了以下代码.

for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) //here
return i;

在我看来,这只有在 size 时才会发生。大于elementData.length 。据我了解,ArrayList#clear实际上并没有减少支持数组的长度。调用addAll应该只是增加其容量,并且 size总是在数组扩展后更新。我不明白这怎么可能处于 size 的状态大于数组的容量。

我注意到的一个特别细节是 Timer 上的延迟为0,表示update()快速连续调用两次。我最好的猜测是addAll调用在某种程度上重叠,离开列表是某种类型的混合无效状态。

如果有人能解释这里发生的事情那就太好了!

最佳答案

如果没有正确的同步,就无法保证一个线程的哪些更改在另一个线程中可见。

无保证意味着如果线程 A 更改 elementDatasize,线程 B 会看到:

  • 没有任何变化
  • 更新的大小,但旧的elementData
  • 更新的 elementData,但旧的 size
  • 值均已更新,但 elementData 的内容没有更新
<小时/>

至于为什么会发生这种情况(请参阅 Wikipedia:Java Memory Model 进行简短介绍):

在现代计算机中,主内存相对较慢,因此主内存和执行当前线程的CPU核心之间存在多级缓存。

线程 A 可能会在 CPU 寄存器或多个缓存之一中保留对 sizeelementData 的更新,并在稍后的某个时间更新主内存(甚至在不同的时间) fields) - 只要线程 A 所做的事情的正确性不受影响。由于没有发生同步,因此无需担心多线程正确性 - 线程 A 的工作方式就好像它是访问这些字段的唯一线程一样。

类似地,线程 B 可以将 sizeelementData 保存在 CPU 寄存器或缓存中,或者它可以根据需要从主内存更新其中之一或两者(可能是缓存)保存 size 的行已被刷新,需要从主内存重新加载 size)。由于没有发生同步,线程 B 假设没有其他线程更改这些字段,因此缓存的值始终与主内存中的值相同。

关于java - ArrayList#contains 上的 ArrayIndexOutOfBounds(多线程),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51054488/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com