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 ?
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 wait
ing Thread
s which is exactly what all Poller
s without elements are doing.
Once it's called, the Poller
s 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.
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
wait/notify
mechanismFor 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.
"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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With