无法从Java共享ArrayList中删除元素,同时添加元素

时间:2017-06-27 12:34:24

标签: java arraylist synchronized

首先我不在这里使用迭代器。

我在共享的ArrayList上使用2个线程,1st用于向ArrayList添加值,其他用于创建它的临时副本并对其执行一些操作,然后从原始列表中删除所有临时元素。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ArraylistTest {

    public static void main(String...ar){
        new AddingThread().start();
        new RemovalThread().start();
    }
}

class RemovalThread extends Thread{
    static List<Integer> originalBigList = new ArrayList<>();

    @Override
    public void run(){
        System.out.println("RemovalThread started");
        while(true){
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("creating copy of originalBigList");
            List<Integer> tempList = new ArrayList<>(originalBigList);
            System.out.println("copied list");
            //
            //some operations on copied temp list
            //
            System.out.println("removing tempList elements after completing operations");
            System.out.println("originalBigList before removing size "+originalBigList.size());
            originalBigList.removeAll(tempList);
            System.out.println("removed!!");
            System.out.println("after size "+originalBigList.size());
        }
    }
}

class AddingThread extends Thread{

    @Override
    public void run(){
        System.out.println("Adding thread started");
        int ctr = 0;
        while(true){
            RemovalThread.originalBigList.add(ctr);
            ctr++;
        }
    }
}

输出: -

Adding thread started
RemovalThread started
creating copy of originalBigList
copied list
removing tempList elements after completing operations
originalBigList before removing size 4102267
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    at java.util.ArrayList.add(ArrayList.java:458)
    at AddingThread.run(ArraylistTest.java:47)

现在我的问题是我在输出中看到正在复制列表的语句被执行,它正在从原始列表中生成临时列表,但是删除语句没有执行而且没有给出任何异常和我&# 39; m使用简单的arraylist不同步,为什么会这样呢?

删除或添加操作时是否有内部锁?如果是,那么Collections.synchronised(arraylist)的用途是什么?

3 个答案:

答案 0 :(得分:2)

以下一行

            List<Integer> tempList = new ArrayList<>(originalBigList);

将通过originalBigList遍历列表构造函数来创建你的tempList,如果AddingThread将在paralel中运行,它将抛出杀死你的RemovalThread的ConcurrentModificationException,也许你在控制台中看不到它,但它可能就在那里。我会为你的originalBigList

使用CopyOnWriteArrayList

答案 1 :(得分:2)

我发现你的问题看下面的代码

 public static void main(String ...args){
    long start = System.currentTimeMillis();
    List<Integer> l1 = new ArrayList<>();
    List<Integer> l2 = new ArrayList<>();

    for(int i = 0 ; i < Integer.MAX_VALUE/10000;i++){
        l1.add(i);
        l2.add(i);
    }

    System.out.println(String.format("Field both arrays in %s seconds",(System.currentTimeMillis()-start)/1000) );
    start = System.currentTimeMillis();
    l1.removeAll(l2);

    System.out.println(String.format("Removed one array from other %s seconds",(System.currentTimeMillis()-start)/1000) );

}

在0秒内对两个数组进行分析

从其他34秒内删除了一个数组

这是一个巨大的数组,现在看看输出删除需要花费大量的时间,在你的情况下,AddingThread使得数组变得庞大,所以它的工作只需要很多时间,同时,AddingThread一直在工作。顺便说一下,为什么需要首先找到每个值需要这么长时间才能理解为什么它是O(n ^ 2)。当我使用HashSet而不是List时注意它是如何工作的(注意:HashSet搜索是O(1))

public class HashSetTest {
public static Object obj = new Object();
public static void main(String... ar) {
    new AddingThread().start();
    new RemovalThread().start();
}

}

class RemovalThread extends Thread {
static Set<Integer> originalBigSet = new HashSet<>();
@Override
public void run() {
    System.out.println("RemovalThread started");
    while (true) {
        try {
            sleep(1000);
            System.out.println("creating copy of originalBigSet");
            Set<Integer> tempList;
            synchronized (HashSetTest.obj) {
                tempList = new HashSet<>(originalBigSet);
            }
            System.out.println("copied list");
            //
            //some operations on copied temp list
            //
            System.out.println("removing tempList elements after completing operations");
            System.out.println("originalBigSet before removing size " + originalBigSet.size());
            synchronized (HashSetTest.obj) {
                originalBigSet.removeAll(tempList);
            }
            System.out.println("removed!!");
            System.out.println("after size " + originalBigSet.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

}

class AddingThread extends Thread {

@Override
public void run() {
    System.out.println("Adding thread started");
    int ctr = 0;
    while (true) {
        synchronized (HashSetTest.obj) {
            RemovalThread.originalBigSet.add(ctr);
        }
        ctr++;
    }
}

}

答案 2 :(得分:1)

与@urag的答案类似(特别是关于O(n²)的说法),您可以通过修改AddingThread来看到效果,因此它只添加了有限数量的项目:

class AddingThread extends Thread{

    @Override
    public void run(){
        System.out.println("Adding thread started");
        int ctr = 0;
        while(true){

          if (ctr < 100_000) {
            RemovalThread.originalBigList.add(ctr);
            ctr++;
           }
      }
  }
}

这导致:

Adding thread started
RemovalThread started
creating copy of originalBigList
copied list
removing tempList elements after completing operations
originalBigList before removing size 100000
removed!!
after size 0

现在,将100_000更改为1_000_000,您将等待很长时间,只看到::

Adding thread started
RemovalThread started
creating copy of originalBigList
copied list
removing tempList elements after completing operations
originalBigList before removing size 1000000