
import java.util.Random;

// this example is concurrent, but NOT parallel
// meaning we express the algorithm in chunks
// that can execute in overlapping time frames
//
// but we also manage those algorithmically concurrent 
// chunks is such a way that no actual speedup is seen
// when executed over a sequential algorithm
//
// in this case the problem is starting a thread
// and then synchronizing main to wait for that
// thread to end before creating and starting another
// thread.  So we might have 100 threads made but
// none are allowed to overlap in time when they execute

public class ConcDemo {
    public static void main(String[] args) {
        // System.out.println(Runtime.getRuntime().availableProcessors());

        for (int num_threads = 1; num_threads <= 24; num_threads++) {
            int num_items = 1000000000;

            Thread[] workers = new Thread[num_threads];

            long start = System.nanoTime();
            for (int i = 0; i < num_threads; i++) {
                int num_per_thread = num_items / num_threads;
                workers[i] = new Thread(() -> {
                    int num_to_do = num_per_thread;
                    Random rnd = new Random();
                    for (int j = 0; j < num_to_do; j++) {
                        rnd.nextDouble();
                    }
                });
                workers[i].start(); // a thread is made and started running
                workers[i].join(); // but then main waits for it to end
		                   // before making another thread
            }

//
//            for (Thread w : workers) {
//                try {
//                    w.join();
//                } catch (InterruptedException e) {
//              }
//            }

            long end = System.nanoTime();

            System.out.println("Overall elapsed with " + num_threads + " threads: " + ((end - start) / 1e9) + " seconds");
        }
    }
}
