Reputation: 31
I am trying to understand concurrency for part of an assignment I'm doing. I think I've got the basics done, but I'm getting stuck in the nuances of my usecase.
I apologize in advance if I haven't mastered the art of the question yet.
Basic idea:
I have a class, of which a few objects are instantiated as representations of parts of a houses and their only main function is to operate(). The catch is that when "Bell" operates, I want the others to wait(). My while statement is based on a bell toggle (boolean). If it is on - wait(), if not, carry on. It seems when running this program, I'm starving the other threaded objects for the sake "Bell".
Any thoughts on where I've gone wrong?
import java.util.ArrayList;
class Test extends Thread {
static int number = 0;
private static final int TIMES = 30;
private static boolean bellstatus = false;
private String name;
private int val;
Test(String y,int x) {
number = number +1;
val = x;
name = new String(y);
}
public void run() { // overrides default run()
for (int i=val; i>0; i--){
System.out.println(name+" run() : " + i);
operate();
}
}
public synchronized void operate(){
System.out.println(name+ " operating");
while(bellstatus){
try{
System.out.println("Bell is ringing");
wait();}catch(InterruptedException e){}
}
if(name.equals("Bell")){
try{
System.out.println("Bell working");
bellstatus = true;
Thread.sleep(500);
Thread.yield();}catch(InterruptedException e){}
bellstatus = false;
notifyAll();
System.err.println("Bell is done");
}
if(name.equals("Door")){
try{
System.out.println("Door working");
Thread.sleep(500);}catch(InterruptedException e){}
}
if(name.equals("Window")){
try{
System.out.println("Window working");
Thread.sleep(500);}catch(InterruptedException e){}
}
}
public static void main(String arg[]) {
ArrayList<Test> example = new ArrayList();
Test a = new Test("Bell",20);
Test b = new Test("Door",20);
Test c = new Test("Window",20);// create thread
example.add(a);
example.add(b);
example.add(c);
System.out.println("Number of objects: "+a.number);
for(int i = 0;i<example.size();i++)
example.get(i).start();
// start thread run
}
}
Upvotes: 3
Views: 87
Reputation: 72884
The javadoc of notifyAll
mentions:
Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods.
However the operate
method is synchronizing on the instance of the thread object. Hence notifyAll
will not wake up the threads that are waiting, since each thread owns a different monitor and is waiting on that monitor.
One way to solve this is by synchronizing on a shared lock:
...
static Object lock = new Object();
...
public void operate() {
synchronized (lock) {
System.out.println(name + " operating");
while (bellstatus) {
try {
System.out.println("Bell is ringing");
lock.wait();
} catch (InterruptedException e) {
}
}
if (name.equals("Bell")) {
try {
System.out.println("Bell working");
bellstatus = true;
Thread.sleep(500);
Thread.yield();
} catch (InterruptedException e) {
}
bellstatus = false;
lock.notifyAll();
System.err.println("Bell is done");
}
if (name.equals("Door")) {
try {
System.out.println("Door working");
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
if (name.equals("Window")) {
try {
System.out.println("Window working");
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
Upvotes: 0
Reputation: 201537
Please don't use Raw Types (you could use the diamond operator <>
). Then, if I understand your question, one possible solution is to use a ReentrantLock
like,
static class Test extends Thread {
static int number = 0;
private static Lock lock = new ReentrantLock();
private String name;
private int val;
Test(String y, int x) {
number = number + 1;
val = x;
name = new String(y);
}
public void run() {
for (int i = val; i > 0; i--) {
System.out.println(name + " run() : " + i);
operate();
}
}
public synchronized void operate() {
System.out.printf("%s operating%n", name);
lock.lock();
try {
System.out.printf("%s working%n", name);
Thread.sleep(500);
System.out.printf("%s done%n", name);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String arg[]) {
List<Test> example = new ArrayList<>();
Test a = new Test("Bell", 20);
Test b = new Test("Door", 20);
Test c = new Test("Window", 20);// create thread
example.add(a);
example.add(b);
example.add(c);
System.out.println("Number of objects: " + a.number);
for (int i = 0; i < example.size(); i++) {
example.get(i).start();
}
// start thread run
}
Upvotes: 1
Reputation: 2038
Use just one thread and a queue of runnables. An executor with just one thread will queue your tasks until the one running is done https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html
Upvotes: 1