HasanAlvi
HasanAlvi

Reputation: 31

Java Threading, suspending multiple other threads while one is running

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

Answers (3)

M A
M A

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

Elliott Frisch
Elliott Frisch

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

eduyayo
eduyayo

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

Related Questions