如何从一个线程循环arraylist,同时从其他线程添加它?

时间:2016-05-09 13:48:36

标签: java multithreading loops arraylist

如何从一个线程循环ArrayList,同时从其他线程添加它?

1)我有一个循环遍历ArrayList并执行一些检查的线程。

2)我有第二个帖子,它添加到ArrayList的结尾。

我有信号量,当第二个运行时暂停第一个线程, 但是我该如何实现这个:

当第二个线程向ArrayList添加内容时,第一个线程将从暂停的位置继续循环而不抛出java.util.ConcurrentModificationException

3 个答案:

答案 0 :(得分:6)

如果您必须使用ArrayList,则必须同步对其的访问权限。但是,这仍然无法使您免于ConcurrentModificationExeptions,因为这并不一定与并发有关。

来自JavaDoc

  

请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出违反对象合同的一系列方法调用,则该对象可能会抛出此异常。例如,如果线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。

如果可以确保只有新项目附加到列表中,您可以只使用索引并在其上手动循环而不使用迭代器。一旦到达队列的末尾,就会从头开始。

ArrayList q = new ArrayList<>();

//thread 1    
Object o = ...;
synchronized(q) {
  q.add(o); //append
}

//thread 2
int i = 0;
synchronized(q) {
  int size = q.size();
  for(; i < size; i++){
    Object o = q.get(i);
    //do something with o
  }
  if(i >= size) {
    i = 0;
  }
}

但是这样做会导致一个有点顺序的行为,因为一个或另一个线程可能在列表上一次操作。唯一的“优势”是线程模型为循环操作增加了一些随机性。所以你也可以跳过同步和并发而只是做

q.add(o); //append 
//you may add a random condition, i.e. time interval, item count random number to trigger the loop process so it don't get exectued on each add
for(Object o : q){
  //do something with o 
}

当然你可以像Nicolas写的那样使用Safe-Copy,但这只是容器结构(列表)的浅层副本,而不是它的内容。因此,如果您这样做,请确保项目是线程安全的或不要修改它们。

如果第二个线程偶尔从列表末尾删除一个项目,则最好使用队列。 Java为此提供了Deque。一个线程可以在一端添加元素,而另一个线程从另一端移除。当您在多个线程中使用它并手动同步时,您应该更好地使用线程安全实现并跳过同步:

Deque q = new ConcurrentLinkedDeque<>();

//thread 1    
Object o = ...;
q.addFirst(o);

//thread 2
while(!q.isEmpty()){
    Object o = q.removeLast();
   //do something with o
}

答案 1 :(得分:1)

假设您无法使用Queue ConcurrentLinkedQueue,请按以下步骤操作:

我假设您的检查速度很快:

主题1:

synchronized(list) {
    for (MyClass obj : list) {
        // check what you want here
    }
}

主题2:

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

我假设您的检查速度很慢:

主题1:

List<MyClass> safeCopy;
synchronized(list) {
    safeCopy = new ArrayList<>(list);
}
for (MyClass obj : safeCopy) {
    // check what you want here
}

主题2:

与上述相同

如果你不经常修改你的列表,你应该使用CopyOnWriteArrayList它已经是线程安全的,读操作非常快,因为没有锁定但是写操作很慢,因为它重建了整个列表

答案 2 :(得分:0)

最简单的解决方案是使用CopyonWritearrayList,它设计精确地用于这种东西,但请注意它比平常的ArrayList慢