【问题标题】:How to get threads to add to only one arrayList of integers with synchronized如何让线程仅添加到一个具有同步的整数数组列表中
【发布时间】:2015-02-15 16:03:54
【问题描述】:

我一直在尝试做一些简单的事情,比如让两个线程添加到一个数组列表中,但无论出于何种原因,我都无法让它工作。我有同步方法并使用 Collections.synchronized 列表,但它仍然显示它正在打印出两个单独的数组。我编写了应该是一个简短的程序来更好地理解运行两个线程来访问一个arrayList。如果有人能阐明我犯了什么错误,将不胜感激!

这里是主类

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RunThreads {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        int[] numbers1 = {0, 2, 4, 6, 8};
        int[] numbers2 = {1, 3, 5, 7, 9};
        executor.execute(new ThreadToRun(numbers1));
        executor.execute(new ThreadToRun(numbers2));
        executor.shutdown();
    }
}

这是 TheadToRun 类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ThreadToRun implements Runnable {
    List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
    private int[] array;

    public ThreadToRun(int[] array) {
        this.array = array;
    }

    public void run() {
        list = adder(array);
        for(int i=0; i<list.size(); i++){
            System.out.print(list.get(i)+"("+i+")"); //print out the element at i and the index 
        }                                            //to see if there are two arrays with the same index
    }

    public  List<Integer> adder(int [] a){
        List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
        for (int array : a) {
            synchronized(list){
                list.add(array);
            }
        }
        return list;
    }
}

【问题讨论】:

    标签: java multithreading arraylist thread-safety synchronized


    【解决方案1】:
    public class ThreadToRun implements Runnable {
        List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
    

    listThreadToRun 的实例成员。每次创建ThreadToRun 时,都会将其list 设置为新的ArrayList。这意味着每个线程都有自己的列表。这两个线程没有共享一个列表。

    有很多方法可以让它共享一个列表,但其中一些不推荐(单例模式),而另一些则非常令人讨厌(static 类成员)。最简洁的方法可能是让列表成为构造函数的参数。然后RunThreads 将创建列表并将其传递给两个线程的构造函数:

        List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
        executor.execute(new ThreadToRun(numbers1, list));
        executor.execute(new ThreadToRun(numbers2, list));
    

    ThreadToRun:

    List<Integer> list;
    private int[] array;
    
    public ThreadToRun(int[] array, List<Integer> list) {
        this.array = array;
        this.list = list;
    }
    

    现在您的线程将使用相同的列表。

    还有一个我最初错过的问题,因为您在 adder 中使用了局部变量 list,这让我感到困惑。看起来adder 创建了第二个列表,您也将其称为list,我认为它将被添加到第一个列表中。但由于这是一个局部变量,它不会在线程之间共享;因此synchronized 是在错误的地方。我不确定你的意图是什么。如果您希望adder 实际添加到主list,那么您不能有一个名为list 的局部变量,因为这将隐藏列表。但是,如果您希望 adder 只是创建一个新列表,并让 run 将该新列表添加到主列表中,则 synchronized 必须在 run 中,围绕添加到主列表的代码。不幸的是,我不太确定你想做什么。

    【讨论】:

      【解决方案2】:

      在这里,您正在创建两个“TheadToRun”实例,每个实例都实例化一个新的 list 实例(如下所述)

      List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
      

      此外,在您的 adder 方法中,您正在执行以下操作:

      List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
      

      这将再次创建一个新列表并覆盖实例中的现有列表。

      可能的解决方案:

      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      
      public class ThreadToRun implements Runnable {
          List<Integer> list;
          private int[] array;
      
          public ThreadToRun(int[] array, List<Integer> list) {
              this.array = array;
              this.list = list;
          }
      
          public void run() {
              for (int array : a) {
                  list.add(array);
              }
          }
      }
      
      
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      
      public class RunThreads {
          public static void main(String[] args) {
              ExecutorService executor = Executors.newCachedThreadPool();
              int[] numbers1 = {0, 2, 4, 6, 8};
              int[] numbers2 = {1, 3, 5, 7, 9};
      
              List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
      
              ThreadToRun t1 = new ThreadToRun(numbers1, list);
              ThreadToRun t2 = new ThreadToRun(numbers1, list);
      
              executor.execute(t1);
              executor.execute(t2);
              executor.shutdown();
      
              // print the list out
              for(int i=0; i<list.size(); i++){
                  System.out.print(list.get(i) + " found at location ("+i+")");
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        您正在为“ThreadToRun”的每个实例创建新的数组列表。所以它们不是同一个列表。

        您需要在可运行对象之间共享一个列表。

        【讨论】:

          【解决方案4】:

          您创建了两个同步集合的实例(在您的ThreadToRun 的第 6 行) - 每个ThreadToRun 实例一个。您必须在所有线程之间共享相同的实例。有几种方法可以做到这一点。例如。这可以通过将列表设为静态(我不喜欢)或通过构造函数将实例传递给类来完成。

          【讨论】:

            【解决方案5】:

            我建议你两件事:

            1. 更重要的一点。您正在调用类似list = adder(array); 的方法。所以它总是会替换你的 ArrayList 的内容。列表中任何较早的数据都将被该方法返回的新列表替换

            取而代之,你可以通过以下方式尝试,

            list.add(adder(array));

            1. 创建列表的另一种方法是将您的列表声明为static。这样,您的两个线程都将通用,并且它们将进入同一个列表。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-06-06
              • 2019-07-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-03-15
              相关资源
              最近更新 更多