Multicore Parallelization in Java

Nowadays, most of the machines have at least 4 core processors. When we program an application, it usually uses one core, which is massive underutilization of the full power of the machine. We look into Java 6 Executor Pattern to improve the speed of an algorithm utilizing multi-core parallelization.

Parallelization consists of dividing a process into a set of smaller tasks, which can be executed in parallel. Once finished, their results can be combined to compute the final result. Each of the task can be run independently on a separate core. Usually, this can be achieved using Multithreading, but it can quickly get coplex and cumbersome. Add the problem of synchronization and deadlocks, and your head starts swirling. Creating and managing threads is an expensive process and if not handled properly, it can end up putting unnecessary load on the system.

ExecutorService, which was introduced in the later versions of Java provides a nice abstraction over threads. It also takes care of synchronization and concurrent access of variables and manages threads internally. So we don’t have to worry about it. Below, we look at a common way to use the ExecutorService to perform a task in parallel.

  1. Create a class that implements the Callable interface. Callable interface has a T call(){} method which returns an object of type T. You can put the code that we want to parallelize in the implemented call method.

    Note: The main difference between Runnable and Callable is that Callable can return a result, which is a huge advantage if we want to use the results from the parallel computation.

    
         class ParallelTask implements Callable<Person> {
    
             @Override
             public Person call() throws Exception {
                 return new Person(); // Person is a dummy class
             }
         }
    
  2. Create an ExecutorService object, to which you submit the instances of the above class. There are different ways to create ExecutorService, but the commmon way is to use the static method newFixedThreadPool(n) on Executors class, where n is the number of threds to create. submit returns an object of type Future, so we can store these objects in a collection of Future.

         List<Future> futureList = new ArrayList<>();
         ExecutorService executorService = Executors.newFixedThreadPool(4);
    
         for(int i=0; i<4; i++)
             futureList.add(executorService.submit(new ParallelTask()));
    
  3. Finally, you can loop throught the futureList, and invoke each task, which can be done using the get method on Future, which waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available.

    
         for(Future future : futureList) {
                 try {
                     Person p = (Person) future.get();
                     /*
                         Deal with the Person
                     */                
                 } catch (InterruptedException e) {}
                 catch (ExecutionException e) {}
             }