gpt4 book ai didi

Java ArrayList 线程不安全示例说明

转载 作者:行者123 更新时间:2023-12-03 12:44:27 25 4
gpt4 key购买 nike

class ThreadUnsafe {

static final int THREAD_NUMBER = 2;
static final int LOOP_NUMBER = 200;

public static void main(String[] args) {
ThreadUnsafe test = new ThreadUnsafe();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
test.method1(LOOP_NUMBER);
}, "Thread" + i).start();
}
}


ArrayList<String> list = new ArrayList<>();

public void method1(int loopNumber) {
for (int i = 0; i < loopNumber; i++) {
method2();
method3();
}
}
private void method2() {
list.add("1");
}
private void method3() {
list.remove(0);
}

}
上面的代码抛出
java.lang.IndexOutOfBoundsException: Index: 0, Size: 1
我知道 ArrayList 不是线程安全的,但在这个例子中,我认为每个 remove() 调用都保证在至少一个 add() 调用之前,所以即使顺序困惑,代码也应该没问题,如下所示:
thread0: method2()
thread1: method2()
thread1: method3()
thread0: method3()
这里需要一些解释,请。

最佳答案

如果总是一个 add()remove()在另一个开始之前通话已完全完成,您的推理是正确的。但是ArrayList不保证,因为它的方法不是 synchronized .因此,可能会发生两个线程同时进行一些修改调用的情况。
让我们看看例如的内部结构add()理解一种可能的故障模式的方法。
添加元素时,ArrayList使用 size++ 增加大小.这不是原子的。
现在想象列表是空的,两个线程 A 和 B 在完全相同的时刻添加一个元素,执行 size++并行(可能在不同的 CPU 内核中)。让我们想象一下事情按以下顺序发生:

  • A 读取大小为 0。
  • B 读取大小为 0。
  • A 将其值加 1,得到 1。
  • B 在其值上加 1,得到 1。
  • A 将其新值写回 size字段,导致 size=1 .
  • B 将其新值写回 size字段,导致 size=1 .

  • 虽然我们有 2 add()电话, size只有 1。如果现在您尝试删除 2 个元素(这次是顺序发生的),第二个 remove()将失败。
    为了实现线程安全,没有其他线程应该能够像 size 那样乱搞内部结构。 (或元素数组),而当前正在进行一次访问。
    多线程本质上是复杂的,因为来自多个线程的调用不仅可以以任何(预期的或意外的)顺序发生,而且它们也可以重叠,除非受到诸如 synchronized 之类的机制的保护。 .另一方面,过度使用同步很容易导致多线程性能不佳,也会导致死锁。

    关于Java ArrayList 线程不安全示例说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64277789/

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