Putting runnable to sleep

Yoda picture Yoda · May 12, 2012 · Viewed 52.2k times · Source

I have a problem with Java. I would like to write a program where there is Class Main which has ArrayList of Threads of some class (Class Task) which just writes a letter and the number. Object Main just wakes one Thread from ArrayList and let it to do something while the same object(Main) sleeps another one.

It works ok: 0A, 0B, 0C, 1B, 1C, 1A, 2B, 2A, 2C, 3B, 3C, 3A, 4B, 4C, 4A, 5B, 5A, 5C,

but only if i Comment the: e.printStackTrace() e is Exception then I get lot of java.lang.IllegalMonitorStateException at java.lang.Object.notify(Native Method) at Main.run(Main.java:22)

So the notify works wrong, how I should correctly wake it, please tell me, show, correct. PLease

import java.util.ArrayList;

import java.util.ArrayList;

public class Main extends Thread {
ArrayList<Thread> threads;

public Main() {
    super();
    threads = new ArrayList<Thread>();
}

public void run() {
    for (int i = 0; i < 3; i++) {
        threads.add(new Thread(new Task(i + 65)));
    }
    long cT = System.currentTimeMillis();
    for (int i = 0; i < threads.size(); i++) {
        threads.get(i).start();
    }
    while (System.currentTimeMillis() - cT < 10000) {
        for (int i = 0; i < threads.size(); i++) {
            try {
                threads.get(i).notify();
                // HOW TO WAKE THREAD FROM threads ArrayList
                Thread.sleep(1000);
                // how to put to bed the same thread ?
                threads.get(i).wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

public static void main(String[] args) {
     new Main().start();
    //new Thread(new Task(65)).start();

}

}

H

public class Task implements Runnable {
int nr;
char character;

public Task(int literaASCII) {
    this.nr = 0;
    character = (char) (literaASCII);
}

@Override
public void run() {
    while (true) {
        try {
            System.out.print(nr + "" + character + ", ");
            nr++;
            int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms)
            Thread.sleep(r);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}



public static void main(String[] args) {
    // TODO Auto-generated method stub

}
}

Answer

rodion picture rodion · May 12, 2012

sleep and wait are very different. sleep simply pauses the current thread for the specified amount of time, and has no direct interaction with other threads.

wait is more complicated: the idea of wait is to pause a thread on a given monitor (or lock, if you wish) and let other thread do work until it notifys on that monitor and releases it. So wait and notify involves interaction between two or more threads.

Because of this interaction, in order for wait and notify to work correctly, the thread which calls those methods must own the monitor (the lock), which means, that object.wait() or object.notify() must be called from within synchronized(object){ ... } block. If you call object.wait() without synchronized-block you will always get an IllegalMonitorStateException.

In your code,

for (int i = 0; i < threads.size(); i++) {
  threads.get(i).start();
} 

this will start all threads and then will run them all at the same time, not one at a time as you want.

To make sure only one thread runs one at a time you will need to pass a common monitor object to all threads and have them wait on that monitor. For example:

public class Main extends Thread {
  //...

  public void run(){
    //Initialize all threads with common monitor object
    Object monitor = new Object();
    for (int i = 0; i < 3; i++) {
      threads.add(new Thread(new Task(i + 65, monitor)));
    }
    long cT = System.currentTimeMillis();
    for (int i = 0; i < threads.size(); i++) {
      //All threads will start, and immediately pause on monitor.wait()
      threads.get(i).start();
    }
    synchronized(monitor){
      while (System.currentTimeMillis() - cT < 10000) {
        //All threads are currently waiting, so we need to wake one random
        //thread up by calling notify on monitor. Other thread will not run yet,
        //because this thread still holds the monitor.
        monitor.notify();

        //Make this thread wait, which will temporarily release the monitor
        //and let the notified thread run.
        monitor.wait();
      }
    }
  }
}

//...

public class Task implements Runnable{
  int nr;
  char character;
  Object monitor;

  public Task(int literaASCII, Object monitor) {
    this.nr = 0;
    this.monitor = monitor;
    character = (char) (literaASCII);
  }

  @Override
  public void run() {
    synchronized(monitor){
      while (true) {
        //Pause this thread and let some other random thread
        //do the work. When other thread finishes and calls notify()
        //this thread will continue (if this thread is picked).
        monitor.wait();

        try {
          System.out.print(nr + "" + character + ", ");
          nr++;
          int r = (int) ((Math.random() * 500) + 500); // <500ms,1000ms)

          Thread.sleep(r);
        } catch (Exception e) {
          e.printStackTrace();
        }

        //This thread has finished work for now. 
        //Let one other random thread know.
        monitor.notify();

        //Other thread will not be able to do work until this thread 
        //releases the monitor by calling monitor.wait() or 
        //completely exists the synchronized(monitor){ ... } block.
      }
    }
  }
}

It may behave slightly differently from your original intention, because threads will wake up randomly, so there is no guarantee the output will be in any particular order.

Also note that in general you should prefer notifyAll() to notify(), unless you have a very good reason to use notify(). Because notify() only wakes up one thread, if that thread "forgets" to call notify at the end, all other threads may wait forever.