多个线程访问ArrayList

时间:2014-07-23 18:15:12

标签: java multithreading arraylist

我有一个用于缓冲数据的ArrayList,以便其他线程可以读取它们

这个数组不断添加数据,因为它是从udp源读取的,其他线程不断从该数组中读取。然后从数组中删除数据。

这不是实际的代码,而是一个简化的例子:

 public class PacketReader implements Runnable{

   pubic static ArrayList<Packet> buffer = new ArrayList() ;
    @Override 
    public void run(){
    while(bActive){
    //read from udp source and add data to the array
  }
 }

 public class Player implements Runnable(){

 @Override 
 public void run(){


 //read packet from buffer
 //decode packets

 // now for the problem :

 PacketReader.buffer.remove(the packet that's been read);
     }
   }

remove()方法从数组中删除数据包,然后将右边的所有数据包移到左边以覆盖空白。

我担心的是:由于缓冲区经常被多个线程添加和读取,因此remove()方法会产生问题,因为它必须将数据包移到左边?

我的意思是如果.add()或.get()方法在该arraylist上被调用,同时正在进行转换会是一个问题吗?

我有时会得到索引超出范围的异常,它的类似于: index:100 size 300,这是奇怪的cuz索引在大小范围内,所以我想知道这是否可能导致问题,或者我应该寻找其他问题。

谢谢

2 个答案:

答案 0 :(得分:6)

听起来你真正想要的是BlockingQueueArrayBlockingQueue可能是一个不错的选择。如果您需要一个无界的队列而不关心额外的内存利用率(相对于ArrayBlockingQueue),LinkedBlockingQueue也可以。

它允许您以线程安全且高效的方式将项目推入并弹出。那些推送和弹出的行为可能不同(当你尝试推送到一个完整的队列时会发生什么,或者从空的队列弹出?),BlockingQueue接口的JavaDocs有一个表格,显示所有这些行为很好。


线程安全List(无论它来自synchronizedList还是CopyOnWriteArrayList)实际上是不够的,因为您的用例使用经典的check-then-act模式,这本质上是活泼的。请考虑以下代码段:

if(!list.isEmpty()) {
    Packet p = list.remove(0); // remove the first item
    process(p);
}

即使list是线程安全的,但这种用法不是!如果list在“if”检查期间有一个元素,但是另一个线程在您到达remove(0)之前将其删除,该怎么办?

可以通过同步两个动作来解决这个问题:

Pattern p;
synchronized (list) {
    if (list.isEmpty()) {
        p = null;
    } else {
        p = list.remove(0);
    }
}
if (p != null) {
    process(p);  // we don't want to call process(..) while still synchronized!
}

这样效率较低,代码比BlockingQueue更多,因此没有理由这样做。

答案 1 :(得分:2)

是的,会出现问题,因为ArrayList不是线程安全的,ArrayList对象的内部状态会被破坏,最终会出现一些不正确的输出或运行时异常。您可以尝试使用synchronizedList(List list),或者如果它很合适,您可以尝试使用CopyOnWriteArrayList

此问题是Producer–consumer problem。您可以通过使用某种类型的锁定从缓冲区中提取对象(在您的情况下为List)来查看有多少人修复它。如果您不一定需要List,也可以查看线程安全缓冲区实现。