Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

multiple threads accessing an ArrayList

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

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);
     }
   }

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

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?

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 ?

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 .

thank you

like image 260
vlatkozelka Avatar asked Jul 23 '14 18:07

vlatkozelka


2 Answers

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.

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.


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);
}

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!
}

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

like image 101
yshavit Avatar answered Sep 17 '22 02:09

yshavit


Yes there would be problems because ArrayList is not thread-safe, the internal state of the ArrayList object would be corrupted and eventually you would have some incorrect output or runtime exceptions appearing. You can try using synchronizedList(List list), or if it's a good fit you could try using a CopyOnWriteArrayList.

This issue is the Producer–consumer problem. You can see how much people fix it by using a lock of some kind taking turns extracting an object out of a buffer (a List in your case). There are thread safe buffer implementations you could look at as well if you don't necessarily need a List.

like image 20
NESPowerGlove Avatar answered Sep 17 '22 02:09

NESPowerGlove