Reputation: 812
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 ?
Upvotes: 6
Views: 4026
Reputation:
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.
Upvotes: 3
Reputation: 10786
"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.
Upvotes: 1
Reputation: 188
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.
Upvotes: 5
Reputation: 166
You can use a queue. A queue has its own poll method. You could make it static but i doubt that is the best approach. Generally I use spring to instantiate the queue in some kind of wrapper class but it doesnt look like you are taking that route.
Upvotes: 0
Reputation: 77
If you want access new value entered by the user from poller object then:
For Example, You can do it this way:
public class UserRequest implements Runnable {
private ArrayList<String> arrayList = new ArrayList<String>();
@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
{
Poller poller = new Poller(arrayList);
Thread t = new Thread(poller);
t.start();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
You can change your Poller class like this:
public class Poller implements Runnable {
private ArrayList arrayList = null;
Poller(ArrayList<String> arrayList){
this.arrayList = arrayList;
}
public void poll()
{
for(String color : arrayList)
{
if(color == "")
{
//do work
}
else
{
//do work
}
}
}
@Override
public void run() {
while(true){
poll();
}
}
But instead of calling pool in a infinity loop you should add a listener to your arrayList so that you call poll()
only when a new value has been added to the List.
You can check out this link to know more about adding listener to a ArrayList
: https://stackoverflow.com/a/16529462/7083385
Upvotes: 0