/**
 * This application demonstrates how to synchronize multiple threads
 * @author hornick
 */
//package two_method_sync;

/**
 * Main class; creates a secondary thread upon creation
 */
public class ThreadSyncDemoApp {

   private Object signal = new Object(); // a generic Object (any Object-derived object can be used) used for synchronization

   /**
    * standard main method; just creates an instance of this application object
    * @param args - not used
    */
   public static void main(String[] args) {
      new ThreadSyncDemoApp();

   }

   /**
    * Default ctor.
    * Creates a secondary thread which executes a method.
    * Ctor then executes another method on the main thread.
    */
   public ThreadSyncDemoApp() {
      Thread t = new Thread(                                                  // create a secondary Thread
                            new Runnable() {                        // create an object that can be run by the Thread (must implement Runnable) 
                               public void run() {             // implement a run() method (makes the object a Runnable)
                                  outputUppercaseMessage("secondary thread");                     // the run() method just calls one of this application's methods
                               }
                            }
                                                                              );
      t.start();                                                      // start the Thread, which causes the method above to execute off the main thread

      outputLowercaseMessage("main thread");
      System.out.println("Main thread done.");
   }

   /**
    * Outputs the given String to the console, in uppercase only.
    * @param msg the String to output to the console
    */
   public void outputUppercaseMessage( String msg ) {
      pause(500);
      while(true) {   // output forever
         synchronized( signal ) {        // only one thread at a time can enter this block, the signal guards the entrance
            for( int c=0; c<msg.length(); c++ ) {  // output the message in uppercase, one char at a time
               System.out.print(msg.toUpperCase().charAt(c));
               pause(10);
            }
            System.out.println();
            signal.notify();                // notify any waiting thread that the this thread is releasing its signal
            try {
               signal.wait();          // wait for another thread to notify this thread to proceed
            } catch( InterruptedException e ) {
               System.out.println("Interrupted outputUppercaseMessage.");
            }
         }
      }               
   }
   /**
    * Outputs the given String to the console, in lowercase only.
    * @param msg the String to output to the console
    */
   public void outputLowercaseMessage( String msg ) {
      for(int i=0; i<10; i++ ) {      // output 10 times
         synchronized( signal ) {        // only one thread at a time can enter this block, the signal guards the entrance
            for( int c=0; c<msg.length(); c++ ) {  // output the message in uppercase, one char at a time
               System.out.print(msg.toLowerCase().charAt(c));
               pause(10);
            }
            System.out.println();                           
            signal.notify();                // notify any waiting thread that the this thread is releasing its signal
            try {
               signal.wait();          // wait for another thread to notify this thread to proceed
            } catch( InterruptedException e ) {
               System.out.println("Interrupted outputLowercaseMessage.");
            }
         }
      }               
   }

        
   /**
    * sleep for the specified number of milliseconds
    * @param t - amount of time to sleep (in ms)
    */
   private void pause( long t ) {
      try {
         Thread.sleep(t);
      } catch( InterruptedException e ) {
         // do nothing
      }
   }
}       
