/*
 * "hello world" program, version 4
 *
 * creates threads using static inner class (which allows creating each
 *   thread with thread-specific information) and Java 1.5 features
 *   (slightly different ones from version 3).
 *
 * number of threads is taken from environment variable NUM_THREADS.
 */
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class Hello4 {

    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 executor for threads */

        ExecutorService executor = Executors.newFixedThreadPool(numThreads);

        /* create tasks and send to executor, creating Future objects
           so we can wait for tasks to finish */

        Future<?>[] results = new Future<?>[numThreads]; 

        for (int i = 0; i < numThreads; ++i) {
            results[i] = executor.submit(new Inner(i));
        }

        /* shut down executor and wait for threads to finish */

        executor.shutdown();
        for (int i = 0; i < numThreads; ++i) {
            try {
                Object o = results[i].get();
            }
            catch (ExecutionException e) {
                System.err.println("should not happen");
            }
            catch (InterruptedException e) {
                System.err.println("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() {
            System.out.println("hello from " + myID);
        }

    }
}