code: public static void run(boolean parallel, int N, IndexRunnable ir, ExecutorService threadPool)
      {
          if(!parallel)
          {
              for(int i = 0; i < N; i++)
                  ir.run(i);
              return;
          }
          
          final CountDownLatch latch = new CountDownLatch(N);
  
          IntStream.range(0, N).forEach(threadID ->
          {
              threadPool.submit(() ->
              {
                  ir.run(threadID);
                  latch.countDown();
              });
          });
  
          try
          {
              latch.await();
          }
          catch (InterruptedException ex)
          {
              Logger.getLogger(ParallelUtils.class.getName()).log(Level.SEVERE, null, ex);
          }
  
      }
docs: This helper method provides a convenient way to break up a computation
  across <tt>N</tt> items into individual indices to be processed. This
  method is meant for when the execution time of any given index is highly
  variable, and so for load balancing purposes, should be treated as
  individual jobs. If runtime is consistent, look at {@link #run(boolean, int, jsat.utils.concurrent.LoopChunkRunner, java.util.concurrent.ExecutorService)
  }.
  
  
  @param parallel a boolean indicating if the work should be done in
  parallel. If false, it will run single-threaded. This is for code
  convenience so that only one set of code is needed to handle both cases.
  @param N the total number of items to process.
  @param ir the runnable over a contiguous range
  @param threadPool the source of threads for the computation