
import java.lang.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;

public class MyFuture {
    public static void main(String[] args) {
        test_start();
        test_execute();
        test_submit();
        test_invokeAll();
        test_fjInvokeAll();
    }
    
    static void test_start() {
        System.out.format("%nTest Thread.start%n");

        Callable<String> cs = new Callable<String>() {
            public String call() {
                try { Thread.sleep(3000); } catch(Exception ex) { };
                String result = "abc";
                return result;
            }
        };

        FutureTask<String> fs = new FutureTask<String>(cs);

        System.out.format("... starting explicit thread%n");
        new Thread(fs).start();

        try { 
            System.out.format("... waiting to get the result%n");
            String res = fs.get();
            System.out.format("got res=%s%n", res);
            
        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
    }

    static void test_execute() {
        System.out.format("%nTest Executor.execute%n");

        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        Callable<String> cs = new Callable<String>() {
            public String call() {
                try { Thread.sleep(3000); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };
                String result = "abc";
                return result;
            }
        };

        FutureTask<String> fs = new FutureTask<String>(cs);

        System.out.format("... executing runnable%n");
        executor.execute(fs); // fs is Runnable        

        try { 
            System.out.format("... waiting to get the result%n");
            String res = fs.get();
            System.out.format("got res=%s%n", res);
            
        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
        
        executor.shutdown();
        // awaitTermination is not really needed here (coz we have already got the result via get())
        // try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };
    }

    static void test_submit() {
        System.out.format("%nTest Executor.submit%n");
        
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        Callable<String> cs = new Callable<String>() {
            public String call() {
                try { Thread.sleep(3000); } catch(Exception ex) { };
                String result = "abc";
                return result;
            }
        };

        System.out.format("... submitting callable%n");
        Future<String> fs = executor.submit(cs);

        try { 
            System.out.format("... waiting to get the result%n");
            String res = fs.get();
            System.out.format("got res=%s%n", res);
            
        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
        
        executor.shutdown();
        // awaitTermination is not really needed here (coz we have already got the result via get())
        // try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };
    }

    static void test_invokeAll() {
        System.out.format("%nTest Executor.invokeAll%n");
        
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        Callable<String> cs = new Callable<String>() {
            public String call() {
                try { Thread.sleep(3000); } catch(Exception ex) { };
                String result = "abc";
                return result;
            }
        };
        
        Callable<String> cs2 = cs; // I'm lazy

        List<Callable<String>> tasks = new ArrayList<Callable<String>>();
        tasks.add(cs);
        tasks.add(cs2);
        
         try { 
            System.out.format("... invoking all callables and%n");
            System.out.format("... waiting for all tasks to complete%n");
            List<Future<String>> futures = executor.invokeAll(tasks);

            System.out.format("... no additional waiting to get results%n");
            for (Future<String> fs: futures) {
                String res = fs.get();
                System.out.format("got res=%s%n", res);
            }
            
        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
        
        executor.shutdown();
        // awaitTermination is not really needed here (coz we have used invokeAll() and get())
        // try { executor.awaitTermination(10, TimeUnit.SECONDS); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };
    }

    static void test_fjInvokeAll() {
        System.out.format("%nTest ForkJoinPool.invokeAll%n");
        
        int procs = Runtime.getRuntime().availableProcessors();
        int maxallowed_threads = procs; // or 2*procs?
        ForkJoinPool fjexecutor = new ForkJoinPool(maxallowed_threads);
        
        Callable<String> cs = new Callable<String>() {
            public String call() {
                try { Thread.sleep(3000); } catch(Exception ex) { };
                String result = "abc";
                return result;
            }
        };
        
        Callable<String> cs2 = cs; // I'm lazy

        List<Callable<String>> tasks = new ArrayList<Callable<String>>();
        tasks.add(cs);
        tasks.add(cs2);
        
         try { 
            System.out.format("... invoking all callables and%n");
            System.out.format("... waiting for all tasks to complete%n");
            List<Future<String>> futures = fjexecutor.invokeAll(tasks);

            System.out.format("... no additional waiting to get results%n");
            for (Future<String> fs: futures) {
                String res = fs.get();
                System.out.format("got res=%s%n", res);
            }
            
        } catch(Exception ex) { 
            System.out.format("*** %s%n", ex.getMessage());
        }
        
        fjexecutor.shutdown();
        // awaitTermination is not really needed here (coz we have used invokeAll() and get())
        // try { fjexecutor.awaitTermination(10, TimeUnit.SECONDS); } catch(Exception ex) { System.out.format("*** %s%n", ex.getMessage()); };
    }
}
            