Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing an ArrayList between two threads?

So I have two threads running where one of them is supposed to get information from the user and the other thread is suppose to work with information supplied by users as follows:

public class UserRequest implements Runnable {

@Override
public void run() {
    // TODO Auto-generated method stub
    String request;
    Scanner input = new Scanner(System.in);
    while(true)
    {
        System.out.println("Please enter request:");
        request = input.nextLine();
        try
        {
            //do something
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

}

And second thread:

public class Poller implements Runnable {

ArrayList<String> colors = new ArrayList<String>();

public void poll()
{
    for(String color : colors)
    {
        if(color == "")
        {
            //do work
        }
        else
        {
            //do work
        }
    }
}

@Override
public void run() {

    colors.add("Violet");
    colors.add("Green");
    colors.add("Yellow");
    colors.add("Orange");

    while(true)
        poll();     
}
}

What I would like to do is take whatever input the user entered inside the UserRequest object and push into the ArrayList in Poller object so it can "work" on the new value as well. I have looked at some thing like BlockingQueue but I don't want either Thread to wait for the other since they have other tasks they need to accomplish in addition to this sharing of data. How can I go about doing this ?

like image 390
Jenna Maiz Avatar asked Oct 30 '16 03:10

Jenna Maiz


3 Answers

Since you've used the verb 'push' and 'poll', it seems you are looking for a Queue not a List.

Therefore, I think you're looking for the ConcurrentLinkedQueue, documented here.

It allows you to have your UserRequest objects feed it and your Poller objects to consume it.

Though it seems your Poller objects will have quite a high CPU consuption because of the open while not having any wait:

public class Poller implements Runnable {
  Queue<String> colors = new ConcurrentLinkedQueue<String>();

  public void poll() {
    while(this.colors.isEmpty()){
      Thread.currentThread().wait();
    }

    String color = this.colors.poll();

    while(color != null) {
      if(color == "") {
        //do work

      } else {
        //do work
      }

      color = this.colors.poll();
    }
  }

  @Override
  public void run() {
    colors.offer("Violet");
    colors.offer("Green");
    colors.offer("Yellow");
    colors.offer("Orange");

    while(true) {

      this.poll();
    }
  }
}

this code needs some changes to run but it contains pretty much everything you need. What it does is very simple: It keeps polling until there are no elements left. Once that happens, the Poller object asks it's current Thread to sleep, since there's no point for it to run without elements in the Queue.

public class UserRequest implements Runnable {

  @Override
  public void run() {
    String request;
    Scanner input = new Scanner(System.in);

    while(true) {
      System.out.println("Please enter request:");
      request = input.nextLine();

      try {
        //do something

      } catch(IOException e) {
        e.printStackTrace();

      } finally {
        this.notifyAll(); // Notifies all sleeping threads to wake up
      }
    }
  }

If you notice, I've only added a notifyAll call to your UserRequest class. Why? Very simple: notifyAll wakes all waiting Threads which is exactly what all Pollers without elements are doing.

Once it's called, the Pollers will wake, check if their color Queue has elements and work with them. If the Queue has no elements, they will sleep again until a UserRequest wakes them up again and so on and so forth.

like image 174
Zeh Avatar answered Nov 10 '22 17:11

Zeh


There are two ways to solve this problem:

1) It's using thread safe collection, like ConccurentLinkedQueue for logic with producer-consumer, jobs consuming or etc. If you want to use the class that implements List interface (and as a consequence you can take methods same to usual ArrayList), you must look to the side of CopyOnWriteArrayList, but note that this class uses blocking synchronization.

2) Another approach is using built-in java synchronization tools, for example

  • Semaphore
  • CyclicBarrier
  • CountDownLatch
  • Locks
  • Phaser
  • Usual wait/notify mechanism

For more details, you must read the specification. Let's consider an example of using Semaphore:

private final Semaphore semaphore = new Semaphore(2, true);

   public void appendToList() throws InterruptedException {
     available.acquire();
     arrayList.add(.....); //put here what u need
   }

   public void putItem(Object x) {
     if (someLogicHere(x)) //semaphore releases counter in this place
       available.release();
   }

Of course, you can combine usage all of them, e.g. you can use a few semaphores simultaneously, or use diff tools.

like image 29
Artem Avatar answered Nov 10 '22 15:11

Artem


"but I don't want either Thread to wait for the other since they have other tasks they need to accomplish in addition to this sharing of data."

There's no way to accomplish this. Any proper threading of the class will always suffer from the problem that you will need to have one thread wait while the other does something. The point though is you want to minimize that. You want to only cause the thread to stall very briefly and rarely and only in those cases where not doing so will cause it to fault. You can use one of the synchronized data structures or you can just write a little bit of synchronization code yourself.

The only object in question is the arraylist, and you want the absolute minimum amount of stall on either thread. So you would want to synchronize it based on the object of the arraylist itself. So just write a couple little synchronization blocks around the points where you access the arraylist object.

public class Poller implements Runnable {

    ArrayList<String> colors;

    public Poller(ArrayList<String> colors) {
        this.colors = colors;
        //pass in colors object, if modified from the scanner side it must synchronize the block around the colors object too.
    }

    public void doWork(String color) {
        //do work
    }

    public void addColor(String color) {
        synchronized (colors) {
            colors.add(color);
        }
    }

    @Override
    public void run() {
        while (!Thread.interrupted())
            if (!colors.isEmpty()) {
                String color;
                synchronized (colors) {
                    color = colors.remove(0);
                }
                doWork(color); //work done outside synch
            }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The point is just never be removing or adding things to the list at the same time. You cannot loop over the list as a whole because if the work is done in-loop it's a problem and the size of the array might change so you don't know how bit it is. But, you can use an ArrayList for this, just synchronize the blocks of code where you change the datastructure and get the string out of that synchronized block and then do work on it. This way the only stall is the brief instant one thread is reading or writing and the other one needs to. Both of which are very fast operations.

like image 1
Tatarize Avatar answered Nov 10 '22 15:11

Tatarize