Java 在多线程中洗牌ArrayList
我有一个静态的ArrayList。每个线程运行来洗牌ArrayList,然后我得到了错误的结果Java 在多线程中洗牌ArrayList,java,arraylist,Java,Arraylist,我有一个静态的ArrayList。每个线程运行来洗牌ArrayList,然后我得到了错误的结果 public void collectoinTest() { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(i); } for (int i = 0; i < 100; i++) { new
public void collectoinTest() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 10000; j++) {
Collections.shuffle(list);
System.out.println( list);
}
}
}).start();
}
}
它有重复的元素,有人能解释吗 您有多个线程更新您的列表。由于您没有使用任何同步,因此可能由于这些线程所做的更改,列表的状态不一致。两个线程可以用两个不同的值更新相同的元素位置,最终得到重复的值 您应该在集合上同步,如下所示:
synchronized (list) {
Collections.shuffle(list);
System.out.println(list);
}
只要不从
run
方法之外更新列表,上述代码就可以工作。如果这样做,可能需要将列表声明为字段或静态成员,然后使用相应的锁。您有多个线程更新列表。由于您没有使用任何同步,因此可能由于这些线程所做的更改,列表的状态不一致。两个线程可以用两个不同的值更新相同的元素位置,最终得到重复的值
您应该在集合上同步,如下所示:
synchronized (list) {
Collections.shuffle(list);
System.out.println(list);
}
只要不从
run
方法之外更新列表,上述代码就可以工作。如果这样做,可能需要将列表声明为字段或静态成员,然后使用相应的锁。洗牌在内部工作的方式是交换列表元素:
T tmp = list.get(from);
list.set(from, list.get(to));
list.set(to, tmp);
如果您有多个线程,线程的操作可能会交错,例如:
T tmp1 = list.get(from1);
T tmp2 = list.get(from2);
list.set(from2, list.get(to2));
list.set(from1, list.get(to1));
list.set(to2, tmp2);
list.set(to1, tmp1);
或
但交织的顺序是不确定的:它取决于许多难以预料的事情。这一点尤其正确,因为有100个线程在同一个列表上工作
某些潜在的交错可能会导致写入不正确的值,因为来自的的值不再是您以前写入的值
考虑一个非常简单的示例:一个列表[0,1]
,以及两个试图交换元素的线程(因此从1=from2=0;到1=to2=1;
)。如果它们像这样交错(就像一个例子;其他交错可能具有相同的效果):
最后的列表将是[0,0]
有两种方法可以避免这种情况:
- 最简单的方法是不要在多个线程中执行;那么线程和自身之间就不会有干扰
- 使线程仅对列表的一部分进行操作(例如,使用
list.subList
提取列表一部分的视图)。这样线程就不会干扰,因为它们在数据的不同部分上进行操作。然而,洗牌将仅限于在这些子列表中交换;元素不能像一次洗牌整个列表一样在列表中移动那么远
shuffle内部工作的方式是交换列表元素:
T tmp = list.get(from);
list.set(from, list.get(to));
list.set(to, tmp);
如果您有多个线程,线程的操作可能会交错,例如:
T tmp1 = list.get(from1);
T tmp2 = list.get(from2);
list.set(from2, list.get(to2));
list.set(from1, list.get(to1));
list.set(to2, tmp2);
list.set(to1, tmp1);
或
但交织的顺序是不确定的:它取决于许多难以预料的事情。这一点尤其正确,因为有100个线程在同一个列表上工作
某些潜在的交错可能会导致写入不正确的值,因为来自
的的值不再是您以前写入的值
考虑一个非常简单的示例:一个列表[0,1]
,以及两个试图交换元素的线程(因此从1=from2=0;到1=to2=1;
)。如果它们像这样交错(就像一个例子;其他交错可能具有相同的效果):
最后的列表将是[0,0]
有两种方法可以避免这种情况:
- 最简单的方法是不要在多个线程中执行;那么线程和自身之间就不会有干扰
- 使线程仅对列表的一部分进行操作(例如,使用
list.subList
提取列表一部分的视图)。这样线程就不会干扰,因为它们在数据的不同部分上进行操作。然而,洗牌将仅限于在这些子列表中交换;元素不能像一次洗牌整个列表一样在列表中移动那么远
这就是所谓的竞争条件。我建议阅读多线程技术。换行:Collections.shuffle(list)代码>与:已同步(YourClassName.class){Collections.shuffle(list);}
并重试。不用说,YourClassName
应该被类的真实名称替换,为什么你要用100个线程来洗牌一个大小为10的列表??为了提高性能??似乎工作分配不好,因为列表是可变的,两个线程可以用同一个元素交换两个不同的元素(竞争条件),这就是为什么你抓狂的原因,当你有100个线程同时洗牌同一个列表时,你怎么能期望它正常工作?这就是所谓的竞争条件。我建议阅读多线程技术。换行:Collections.shuffle(list)代码>与:已同步(YourClassName.class){Collections.shuffle(list);}
并重试。不用说,YourClassName
应该被类的真实名称替换,为什么你要用100个线程来洗牌一个大小为10的列表??为了提高性能??似乎工作分配不好,因为列表是可变的,两个线程可以用同一个元素交换两个不同的元素(竞争条件),这就是为什么你抓狂的原因,当你有100个线程同时洗牌同一个列表时,你怎么能期望它正常工作?