import java.util.List;
import java.util.Vector;
import java.util.Iterator;

/**
 * This is a more sophisticated use of threads, including the ability to
 * pause, re-start, and terminate sets of threads.  Other than the
 * shared thread control flags, however, the threads themselves don't
 * access any common data, so no serious synchronization is necessary.
 * Still, some tricky synchronization is necessary to get the wait()/
 * notify() pairs to work right.
 *
 * @author Terran Lane
 * @version 1.0
 */
public class ThreadDemo1 {

  public static void main(String[] args) {
    if (args.length<1) {
      System.err.println("Usage:");
      System.err.println("\tThreadDemo nThreads");
      System.exit(1);
    }
    ThreadDemo1 td=new ThreadDemo1(Integer.valueOf(args[0]).intValue());
    try {
      Thread.currentThread().sleep(1300);
      td._pauseAll();
      System.out.println("All threads paused.  Waiting 1400ms to be sure.");
      Thread.currentThread().sleep(1400);
      System.out.println("Resuming runs where they left off...");
      td._resumeAll();
      Thread.currentThread().sleep(10000);
      System.out.println("Ending all threads...");
      td._endAll();
      System.out.println("Done...");
    }
    catch (InterruptedException e) {
      System.err.println("Unexpected interruption exception in main " +
			 "thread.  Ending all sub-threads...");
      td._endAll();
    }
  }

  /* ******************** end of public interface ******************** */

  // Most collections have non-synchronized methods.  If all of the threads
  // were going to access this var, it would have to be synchronized through
  // some sort of accessor methods.  As it is, it will only be accessed from
  // the "main" thread, so it's safe to leave unsynchronized.
  private List _thrList;
  private static final int MAX_ITER=10;
  // These two flags are shared by all threads and are used to indicate that
  // a thread should pause, resume, or end.  Atomic access to booleans is
  // guaranteed, but they must be marked volatile to assure timely access in
  // all threads.
  volatile private boolean _thrPauseP;
  volatile private boolean _thrEndP;

  private ThreadDemo1(int nt) {
    if (nt<=0) {
      throw new IllegalArgumentException("Number of threads must be >0");
    }
    _thrList=new Vector(nt);
    _thrPauseP=false;
    _thrEndP=false;
    for (int i=0;i<nt;++i) {
      Thread t=new _localThread("Thread[" + i + "]",(i+1)*500);
      _thrList.add(t);
      t.start();
    }
  }

  private synchronized void _pauseAll() {
    _thrPauseP=true;
    notify();
  }

  private synchronized void _resumeAll() {
    _thrPauseP=false;
    notify();
  }

  private synchronized void _endAll() {
    _thrEndP=true;
    notify();
  }

  // Note: The inner class must now be _non_ static so that all threads can
  // have access to the _thrPauseP and _thrEndP variables in the parent class
  // (ThreadDemo1)
  private class _localThread extends Thread {
    public _localThread(String name,int delay) {
      assert name!=null;
      _name=name;
      _delay=delay;
      _startTime=System.currentTimeMillis();
    }

    public void run() {
      int nIter=0;
      System.out.println("Starting " + _name);
      while (nIter++<MAX_ITER && !_thrEndP) {
	try {
	  sleep(_delay);
	  synchronized (ThreadDemo1.this) {
	    while (_thrPauseP && !_thrEndP) {
	      ThreadDemo1.this.wait();
	    }
	  }
	}
	catch (InterruptedException e) {
	  // In this version, this will happen every time _pauseAll(),
	  // _resumeAll(), or _endAll() is called.
	  System.err.println(_name + "interrupted.");
	}
	long now=System.currentTimeMillis();
	System.out.println(_name + ": After " + nIter + " iteration(s); " +
			   " elapsed time=" + (now-_startTime) + "ms");
      }
      if (nIter>=MAX_ITER) {
	System.out.println(_name + " terminated because of exceeding " +
			   "MAX_ITER");
      }
      if (_thrEndP) {
	System.out.println(_name + " terminated because thread end " +
			   " signalled by _endAll()");
      }
      // when run() returns, this thread terminates
    }

    /* *************** end of public interface *************** */

    private final String _name;
    private final int _delay;
    private final long _startTime;
  }
}
