gpt4 book ai didi

java - 在多线程程序中更新共享资源

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

谁能解释一下下面程序的输出:

public class DataRace extends Thread {
static ArrayList<Integer> arr = new ArrayList<>();

public void run() {
Random random = new Random();
int local = random.nextInt(10) + 1;
arr.add(local);
}

public static void main(String[] args) {
DataRace t1 = new DataRace();
DataRace t2 = new DataRace();
DataRace t3 = new DataRace();
DataRace t4 = new DataRace();

t1.start();
t2.start();
t3.start();
t4.start();

try {
t1.join();
t2.join();
t3.join();
t4.join();

} catch (InterruptedException e) {
System.out.println("interrupted");
}

System.out.println(DataRace.arr);

}
}

输出:

  • [8, 5]
  • [9, 2, 2, 8]
  • [2]

我无法理解输出中不同数量的值。我希望主线程要么等到所有线程都完成执行,因为我将它们加入 try-catch block ,然后输出四个值,每个线程一个,或者在中断的情况下打印到控制台。这两者都没有真正发生在这里。

如果这是由于多线程中的数据争用,它在这里如何发挥作用?

最佳答案

主要问题是多个线程同时向同一个共享ArrayList 添加内容。 ArrayList不是线程安全的。来自 source可以阅读:

Note that this implementation is not synchronized.
If multiple threadsaccess an ArrayList instance concurrently, and at least one of thethreads modifies the list structurally, it must be synchronizedexternally. (A structural modification is any operation that adds ordeletes one or more elements, or explicitly resizes the backing array;merely setting the value of an element is not a structuralmodification.) This is typically accomplished by synchronizing on someobject that naturally encapsulates the list. If no such object exists,the list should be "wrapped" using the Collections.synchronizedListmethod. This is best done at creation time, to prevent accidentalunsynchronized access to the list:

每次调用时都在你的代码中

arr.add(local);

add 方法实现中,跟踪数组 size 的变量将被更新。下面显示了 ArrayListadd 方法的相关部分:

private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1; // <--
}

其中变量字段size是:

/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;

请注意,add 方法同步 和变量size 都没有用volatile 子句标记。因此,适合竞争条件

因此,因为你没有ensure mutual exclusion关于对该 ArrayList 的访问(例如,使用 synchronized 子句包围对 ArrayList 的调用),以及因为 ArrayList 不确保 size 变量更新原子,每个线程可能看到(或看不到)该变量的最后更新值.因此,线程可能会看到 size 变量的过时值,并将元素添加到其他线程之前已经添加的位置。在 extreme 中,所有线程最终可能会将一个元素添加到同一位置(例如, 作为您的输出之一 [2]) .

上述竞争条件导致undefined behavior ,因此原因:

System.out.println(DataRace.arr);

在不同的代码执行中输出不同数量的元素。

要使 ArrayList 线程安全或替代方案,请查看以下 SO 线程:How do I make my ArrayList Thread-Safe? , 它展示了 Collections.synchronizedList(). 的使用, CopyOnWriteArrayList等等。

确保对 arr 结构的访问互斥的示例:

public void run() {
Random random = new Random();
int local = random.nextInt(10) + 1;
synchronized (arr) {
arr.add(local);
}
}

或:

static final List<Integer> arr = Collections.synchronizedList(new ArrayList<Integer>());

public void run() {
Random random = new Random();
int local = random.nextInt(10) + 1;
arr.add(local);
}

关于java - 在多线程程序中更新共享资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65742753/

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