0 java multithreading loops arraylist
如何ArrayList从一个线程循环,同时从其他线程添加它?
1)我有一个循环ArrayList并执行一些检查的线程.
2)我有第二个线程添加到结尾ArrayList.
我有信号量,当第二个线程运行时暂停第一个线程,但我该如何实现:
当第二个线程添加某些东西时ArrayList,第一个线程将从暂停的点继续循环而不抛出java.util.ConcurrentModificationException?
如果你一定要使用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)