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

0 java multithreading loops arraylist

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

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

2)我有第二个线程添加到结尾ArrayList.

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

当第二个线程添加某些东西时ArrayList,第一个线程将从暂停的点继续循环而不抛出java.util.ConcurrentModificationException?

Ger*_*cke 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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 
}
Run Code Online (Sandbox Code Playgroud)

当然你可以像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
}
Run Code Online (Sandbox Code Playgroud)