/*
 * simple synchronization example, version 4:
 *
 * threads perform I/O with delays, introducing need to ensure one-at-a-time
 *   access to System.out.
 *
 * correct synchronization, another way.
 *
 * number of threads is taken from environment variable NUM_THREADS.
 */
public class HelloSynch4 {

    /* main method */
    public static void main(String[] args) {

         /* get number of threads from environment variable */

        int numThreads = 0;
        try {
            String s = System.getenv("NUM_THREADS");
            if (s == null) {
                numThreads = 1;
            }
            else {
                numThreads = Integer.parseInt(s);
            }
        }
        catch (NumberFormatException e) {
            System.out.println("non-numeric value for NUM_THREADS");
            System.exit(1);
        }

        /* create threads */

        Thread[] threads = new Thread[numThreads];

        for (int i = 0; i < numThreads; ++i) {
            threads[i] = new Thread(new Inner(i));
        }

        /* start them up */

        System.out.println("starting threads");

        for (int i = 0; i < numThreads; ++i) {
            threads[i].start();
        }

        /* wait for them to finish */

        for (int i = 0; i < numThreads; ++i) {
            try {
                threads[i].join();
            }
            catch (InterruptedException e) {
                System.err.println("this should not happen");
            }
        }

        System.out.println("threads all done");
    }

    /* inner class containing code for each thread to execute */

    private static class Inner implements Runnable {

        private int myID;

        public Inner(int myID) {
            this.myID = myID;
        }

        public void run() {
            /* synchronize on class object */
            synchronized (getClass()) {
                /* force delay in assembling/printing line */
                System.out.print("hello from thread ");
                try {
                    Thread.sleep(10);
                }
                catch (InterruptedException e) {
                    System.err.println("this should not happen");
                }
                System.out.println(myID);
            }
        }
    }
}