1

I am writing a REST API in Java 7 to retrieve three items from database and send all three together to user as a response. When program runs in the sequential flow i.e. if I fetch one item then another then it is taking legitimate time for execution. But when I use multi-threading i.e. using three threads to fetch three items from database, it is taking more time for execution as compared to sequential. This issue is leading high CPU usage more than 90%.

example:

     Sequential: number of users 60
                 average execution time- 5149 milliseconds

     Parallel:   number of users 60
                 average execution time- 9544 milliseconds

I am using ExecutorService to achieve asynchronous execution mechanism and Countdown latch for synchronization of threads.

Why parallel execution taking more time then sequential? Is there any drawback of using Countdown latch here?

Code for execution of worker threads using Countdown latch :

List<Runnable> workers;

if (StringUtils.isBlank(typeCode)) {
    workers =          
    ItemManagerHelper.determineItemLookupWorkersByItemIdentifiers(Id, ids,
    effectiveStartDate, effectiveEndDate, errors, ItemUIList, dataMap);
    }
else {
     workers = ItemManagerHelper.determineItemLookupWorkersByItemType(Id, 
     ids,effectiveStartDate,effectiveEndDate, typeCode, errors, ItemUIList, 
     dataMap);
    }

ExecutorService threadPoolExecutor = Executors.newFixedThreadPool                                            
(workers.size());

CountDownLatch latch = new CountDownLatch(workers.size());

for (Runnable worker : workers) {
      ((ItemLookupThread) worker).setLatch(latch);
      threadPoolExecutor.submit(worker);
}

try {
      latch.await(threadTimeOut, TimeUnit.MILLISECONDS);
} 
catch (InterruptedException e) {
      error(this.getClass(), e, "Exception occurred while waiting for the 
           lookup child threads completion.");
} 
finally {
      threadPoolExecutor.shutdown();
}

ItemLookupThread is my thread class:

public class ItemLookupThread implements Runnable {

private ItemProvider provider;
private long Id;
ItemsIdentifiers ids;
private long effectiveStartDate;
private long effectiveEndDate;
private Map<Object, Object> dataMap;
private List<BaseResponse.Error> errors;
private List<ItemUI> Items;
private CountDownLatch latch;

  public ItemLookupThread (ItemProvider provider, long Id, 
    ItemsIdentifiers ids,long effectiveStartDate,
    long effectiveEndDate, Map<Object, Object> dataMap,      
    List<ItemUI> Items, List<BaseResponse.Error> errors) {

      this.provider = provider;
      this.Id = Id;
      this.ids = ids;
      this.effectiveStartDate = effectiveStartDate;
      this.effectiveEndDate = effectiveEndDate;
      this.dataMap = dataMap;
      this.Items = Items;
      this.errors = errors;
    }

 @Override
 public void run() {
    try {
       debug(this.getClass(), "Item lookup started :" + 
       Thread.currentThread().getId());
       provider.lookup(Id, ids, effectiveStartDate, effectiveEndDate, 
       dataMap, Items, errors);
       debug(this.getClass(), "Item lookup completed :" + 
       Thread.currentThread().getId());
    }

    finally {
         if (latch != null) {
         latch.countDown();
         }
    }
  }

  public void setLatch(CountDownLatch latch) {
          this.latch = latch;
  }
}
  • 1
    Please share more details - code snippets, what you changed, how you're using the latch, what the threads are doing, etc. As is your question is similar to "My car was making a weird noise, so I drove faster and it got worse, what's wrong?" – dimo414 Apr 05 '17 at 05:33
  • There are plenty of possibilities here, if you spawn too many threads for instance, the creation overhead as well as the context switching can consume a lot of resources, leading to a degradation of the observed throughput. As mentioned by @dimo414 without any other details, we can only hypothesize that either you are misusing threads, or that your application cannot be parallelized efficiently – Adonis Apr 05 '17 at 08:40

1 Answers1

0

My guess is that there is a huge amount of context switching going on. My recommendation is to move the ExecutorService from being a method variable to being a field in your service (or controller) class.

private ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(20);


public void myServiceMethod() {

    List<Runnable> workers;

    if (StringUtils.isBlank(typeCode)) {
        workers =          
        ItemManagerHelper.determineItemLookupWorkersByItemIdentifiers(Id, ids,
        effectiveStartDate, effectiveEndDate, errors, ItemUIList, dataMap);
        }
    else {
         workers = ItemManagerHelper.determineItemLookupWorkersByItemType(Id, 
         ids,effectiveStartDate,effectiveEndDate, typeCode, errors, ItemUIList, 
         dataMap);
        }
    CountDownLatch latch = new CountDownLatch(workers.size());

    for (Runnable worker : workers) {
          ((ItemLookupThread) worker).setLatch(latch);
          threadPoolExecutor.submit(worker);
    }

    try {
          latch.await(threadTimeOut, TimeUnit.MILLISECONDS);
    } 
    catch (InterruptedException e) {
          error(this.getClass(), e, "Exception occurred while waiting for the 
               lookup child threads completion.");
    } 
    finally {
          threadPoolExecutor.shutdown();
    }
}
Jose Martinez
  • 11,452
  • 7
  • 53
  • 68