Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在多线程中洗牌ArrayList_Java_Arraylist - Fatal编程技术网

Java 在多线程中洗牌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

我有一个静态的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 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个线程同时洗牌同一个列表时,你怎么能期望它正常工作?