多个线程访问一个 ArrayList [英] multiple threads accessing an ArrayList

查看:30
本文介绍了多个线程访问一个 ArrayList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

i have an ArrayList that's used to buffer data so that other threads can read them

这个数组不断地有数据添加到它,因为它从一个 udp 源读取,其他线程不断地从那个数组中读取.然后从数组中删除数据.

this array constantly has data added to it since it's reading from a udp source, and the other threads constantly reading from that array.Then the data is removed from the array.

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

this is not the actual code but a simplified example :

 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() 方法从数组中移除数据包,然后将所有右侧的数据包向左移动以覆盖空白.

The remove() method removes packets from the array and then shifts all the packets on the right to the left to cover the void.

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

My concern is : since the buffer is constantly being added to and read from by multiple threads , would the remove() method make issues since its gonna have to shift packets to the left?

我的意思是,如果在该数组列表上调用 .add() 或 .get() 方法,同时进行转换会不会有问题?

i mean if .add() or .get() methods get called on that arraylist at the same time that shift is being done would it be a problem ?

我有时确实会得到索引越界异常,它类似于:索引:100 大小 300,这很奇怪,因为索引在大小范围内,所以我想知道这是否可能导致问题,或者我应该寻找其他问题.

i do get index out of bounds exception sometimes and its something like : index : 100 size 300 , which is strange cuz index is within size , so i want to know if this is what may possibly be causing the problem or should i look for other problems .

谢谢

推荐答案

听起来您真正想要的是 BlockingQueue.ArrayBlockingQueue 可能是个不错的选择.如果你需要一个无界队列并且不关心额外的内存使用(相对于ArrayBlockingQueue),LinkedBlockingQueue 也有效.

It sounds like what you really want is a BlockingQueue. ArrayBlockingQueue is probably a good choice. If you need an unbounded queue and don't care about extra memory utilization (relative to ArrayBlockingQueue), LinkedBlockingQueue also works.

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

It lets you push items in and pop them out, in a thread-safe and efficient way. The behavior of those pushes and pops can differ (what happens when you try to push to a full queue, or pop from an empty one?), and the JavaDocs for the BlockingQueue interface have a table that shows all of these behaviors nicely.

线程安全的List(无论它来自synchronizedList 还是CopyOnWriteArrayList)实际上还不够,因为您的用例使用经典的检查然后行动模式,这本质上是活泼的.考虑这个片段:

A thread-safe List (regardless of whether it comes from synchronizedList or CopyOnWriteArrayList) isn't actually enough, because your use case uses a classic check-then-act pattern, and that's inherently racy. Consider this snippet:

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

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

Even if list is thread-safe, this usage is not! What if list has one element during the "if" check, but then another thread removes it before you get to remove(0)?

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

You can get around this by synchronizing around both actions:

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 相比,这效率较低且需要更多代码,因此没有理由这样做.

This is less efficient and takes more code than a BlockingQueue, though, so there's no reason to do it.

这篇关于多个线程访问一个 ArrayList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆