将元素添加到具有多个线程的列表中:如何使其成为线程安全的?

时间:2014-12-15 15:53:10

标签: java concurrency java.util.concurrent

我通过ExecutorService使用多个线程将对象添加到具有多个for循环的列表中。由于普通的ArrayList不是线程安全的,我使用的是CopyOnWriteList。但是,在下面的代码示例中添加元素并没有给我一个(n1 + n2)元素的列表,所以不知何故存在并发问题。

我思考的错误在哪里? CopyOnWriteList是线程安全列表的正确选择吗?

    public class myThread{

         List<SomeObject> list;

         public myThread(List<someObject> list){
              this.list = list;
         }

         public void run(){
             SomeObject instance = new SomeObject();
             list.add(instance);
         }
     }

     public static void main(String[] args) {

         CopyOnWriteArrayList<someObject> syncList = new CopyOnWriteList<someObject>();

         ExecutorService executor = Executors.newFixedThreadPool(4);

         for(int i=0; i< n1; i++){
             executor.submit(new myThread(syncList))
         }

         for(int i=0; i< n2; i++){
            executor.submit(new myThread(syncList))
         }
      }

3 个答案:

答案 0 :(得分:3)

CopyOnWriteArrayList是一个线程安全列表。

public class myThread implements Runnable {

    List<String> list;

    public myThread(List<String> list){
        this.list = list;
    }

    public void run(){
        String instance = "";
        list.add(instance);
    }


    public static void main(String[] args) throws Exception {

        CopyOnWriteArrayList<String> syncList = new CopyOnWriteArrayList<String>();

        ExecutorService executor = Executors.newFixedThreadPool(4);

        for(int i=0; i< 10; i++){
            executor.submit(new myThread(syncList));
        }

        for(int i=0; i< 20; i++){
            executor.submit(new myThread(syncList));
        }

        executor.shutdown();

        while (executor.awaitTermination(1, TimeUnit.DAYS)) {
            break;
        }

        System.out.println(syncList.size());
    }
}

你试试这个。您可以在完成所有线程之前致电。

答案 1 :(得分:0)

上面的代码不起作用,因为你从未将列表传递给线程(因为它不是extend Thread而不是线程)。尝试

...new myThread(syncList)...

然后它会工作,但如果列表变得非常大,性能可能会很慢。在这种情况下,最好在列表上进行同步:

synchronized(list) { list.add(instance); }

synchronized在这里运作良好。请注意,元素可能在列表中以奇数顺序出现。这是因为两个线程都在“相同”时间添加元素,因此您不会看到n1+n2,而是n1n2元素的混合。 n1中所有元素的顺序都是相同的,但n2之间的元素会混合在一起。

答案 2 :(得分:0)

我的想法出错在哪里?

您没有在线程中共享列表。你的代码甚至无法编译。要解决此问题,请将列表传递给myThread构造函数:

for(int i=0; i< n1; i++){
      executor.submit(new myThread(syncList))
}

CopyOnWriteList是否是线程安全列表的正确选择?

当读取次数远大于写入次数时,最好使用

CopyOnWriteList。您的情况似乎正好相反,因此最好使用java.util.Collections.synchronizedList()

注意:来自Java Concurency in Practice:

CopyOnWriteArrayList is a concurrent replacement for a synchronized
List that offers better concurrency in some common situations and 
eliminates the need to lock or copy the collection during iteration.