多个线程执行相同的方法(非同步)行为
Multiple Threads executing same Method (Non Synchronized) behavior
我从多个线程调用了一个将项目添加到列表的方法,该方法未同步
static List<String> list = new ArrayList<String>();
static void addItem(int itemNo){
list.add("item "+itemNo);
}
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++)
{
int itemNo = i;
Runnable task = () -> addItem(itemNo);
executor.execute(task);
}
executor.shutdown();
System.out.println(list);
}
打印列表的输出是随机的,每次我执行这段代码
我-e
[null, item 1, item 4, item 5, item 6, item 7, item 8, item 9]
[item 1, null, item 2, item 4, item 5, item 3, item 6, item 7, item 8, item 9]
[item 1, item 2, item 3, item 4, item 5, item 6, item 7, item 8, item 9]
在第一个和第二个输出中,有空项,第一个输出中有第一个,第二个输出中有第二个。而且列表的大小也不相同。
据我了解,在多线程中,项目不会按顺序排列。我不知道在添加数组
时这些项目有时会为空或被跳过
我想了解此行为,为什么将空项添加到数组以及为什么在我的方法未 Synchronized
时跳过项
您宝贵的回答对我有很大帮助,
提前致谢
因为添加一个项目不是一个原子操作,它需要多个操作,例如递增大小并将值分配给数组位置。
在多线程竞争条件下,有时两个线程可能同时递增,所以它只递增一次,而不是两次,因此最终大小小于 10。
有时两个线程在赋值之前都会按顺序递增,所以一个位置被跳过,两个线程都赋值给同一个位置,一个线程覆盖另一个线程的值,因此一个空值和一个缺失值。
简而言之,对象的多线程使用,如 ArrayList
,将破坏 列表。永远不要这样做。为什么看到你所做的结果并不重要,只是不要这样做。
List.add(Object)
函数不是原子函数。也就是说,如果多个线程试图将一个元素添加到同一个列表中,它们可能会相互干扰并破坏添加函数的内部工作(调整内部数据结构的大小,计算列表中的项目位置,... ).这可能会导致意外输出甚至严重问题(例如,在未正确同步的情况下跨线程使用 HashMap
- 即使是只读操作)。
如果你使用同步 List
:
static List<String> list = Collections.synchronizedList(new ArrayList<String>());
这将对 list
实例 synchronized
进行方法调用,使添加成为一个有效的原子操作。通过此更改,项目仍然不会按顺序添加,但至少可以确保所有项目都将正确添加。
我从多个线程调用了一个将项目添加到列表的方法,该方法未同步
static List<String> list = new ArrayList<String>();
static void addItem(int itemNo){
list.add("item "+itemNo);
}
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++)
{
int itemNo = i;
Runnable task = () -> addItem(itemNo);
executor.execute(task);
}
executor.shutdown();
System.out.println(list);
}
打印列表的输出是随机的,每次我执行这段代码
我-e
[null, item 1, item 4, item 5, item 6, item 7, item 8, item 9]
[item 1, null, item 2, item 4, item 5, item 3, item 6, item 7, item 8, item 9]
[item 1, item 2, item 3, item 4, item 5, item 6, item 7, item 8, item 9]
在第一个和第二个输出中,有空项,第一个输出中有第一个,第二个输出中有第二个。而且列表的大小也不相同。
据我了解,在多线程中,项目不会按顺序排列。我不知道在添加数组
时这些项目有时会为空或被跳过我想了解此行为,为什么将空项添加到数组以及为什么在我的方法未 Synchronized
时跳过项您宝贵的回答对我有很大帮助, 提前致谢
因为添加一个项目不是一个原子操作,它需要多个操作,例如递增大小并将值分配给数组位置。
在多线程竞争条件下,有时两个线程可能同时递增,所以它只递增一次,而不是两次,因此最终大小小于 10。
有时两个线程在赋值之前都会按顺序递增,所以一个位置被跳过,两个线程都赋值给同一个位置,一个线程覆盖另一个线程的值,因此一个空值和一个缺失值。
简而言之,对象的多线程使用,如 ArrayList
,将破坏 列表。永远不要这样做。为什么看到你所做的结果并不重要,只是不要这样做。
List.add(Object)
函数不是原子函数。也就是说,如果多个线程试图将一个元素添加到同一个列表中,它们可能会相互干扰并破坏添加函数的内部工作(调整内部数据结构的大小,计算列表中的项目位置,... ).这可能会导致意外输出甚至严重问题(例如,在未正确同步的情况下跨线程使用 HashMap
- 即使是只读操作)。
如果你使用同步 List
:
static List<String> list = Collections.synchronizedList(new ArrayList<String>());
这将对 list
实例 synchronized
进行方法调用,使添加成为一个有效的原子操作。通过此更改,项目仍然不会按顺序添加,但至少可以确保所有项目都将正确添加。