An Executor with blocking submit()

Today we are going to talk about an executor that will be blocking on submit if n number of threads are already executing. Suppose if you have a threads running in parallel and they are resource intensive, you may want to limit the number of threads executing. This is what we will do in this tutorial.

  Here is the BlockingQueueExecutor. For this executor you can pass the maximum number of threads that can be either executing or waiting at a particular point in time. And you can also pass maximum number of threads that can run in parallel. And also the maximum keep alive time of the threads.

 

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.logging.log4j.Logger;

public class BlockingQueueExecutor {
 private static Logger logger = org.apache.logging.log4j.LogManager.getLogger(BlockingQueueExecutor.class);

 private static int poolNumber = 1;
 private BlockingQueueExecutor() {
  // rem: No instances
 }
 public static ExecutorService createExecutor(int maximumAcceptableThreads, int nParallelThreads, int keepAliveTimeMS){
  BasicThreadFactory factory = new BasicThreadFactory.Builder()
        .namingPattern("bqe-pool-"+poolNumber+"-thread-%d").build();
  final BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(maximumAcceptableThreads);
  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(nParallelThreads, nParallelThreads,
    keepAliveTimeMS, TimeUnit.MILLISECONDS, queue,factory);
  // by default (unfortunately) the ThreadPoolExecutor will throw an exception
  // when you submit the more than maximumAcceptableThreads job, to have it block you do:
  threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // this will block if the queue is full
        try {
    executor.getQueue().put(r);
   } catch (InterruptedException e) {
    logger.error("error while adding process to queue:", e);
    // keep the interrupt status
    Thread.currentThread().interrupt();
   }
     }
  });
  return threadPool;
 }
}

Here is the singleton executor

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.apache.logging.log4j.Logger;


class RequestExecutor {
 
 private static final Logger logger = org.apache.logging.log4j.LogManager.getLogger(RequestExecutor.class);
 /**
  * Maximun threads allowed in queue, after that the queue waits
  */
 private static final int MAX_THREADS_IN_QUEUE  = 200;
 
 private static final int MAX_CUNCURRENT_THREADS =  16;
 
 /**
  * Keep alive time for threads
  */ 
 private static final int KEEP_ALIVE_TIME_MS =  1000000;
 
 private ExecutorService executorService;
 
 private static final RequestExecutor requestExecutor = new RequestExecutor(); ;
 
 public RequestExecutor getExecutorService() {
  return requestExecutor;
 }
  
 public void init()
 {
  logger.info("init executorService");
  executorService = BlockingQueueExecutor
    .createExecutor(MAX_THREADS_IN_QUEUE,
      MAX_CUNCURRENT_THREADS,
      KEEP_ALIVE_TIME_MS);
 }
 
 public void destroy()
 {
  logger.info("shutting down executorService");
  executorService.shutdownNow();
  logger.info("shutdown of executor service done");
 }

 
 private RequestExecutor() {
  init();
  Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
   @Override
   public void run() {
    executorService.shutdown();
   }
  }));
 }
 
   <T> Future<T> submit(Callable<T> task){
   return executorService.submit(task);
  }
 
}

Here is a simple test to test the executor

 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {

 public static void main(String[] args) throws ExecutionException {
  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100);
  for (int i = 0; i <= 500; i++) {
   fixedThreadPool.submit(new Runnable() {
    @Override
    public void run() {
     try {
      printHello();
     } catch (TimeoutException e) {
         e.printStackTrace();
     }
    }
   });
  }
  
  fixedThreadPool.shutdown();
  System.out.println("shutdown called");
  try {
   fixedThreadPool.awaitTermination(100, TimeUnit.SECONDS);
  } catch (InterruptedException e) {
   Thread.currentThread().interrupt();
  }
  RequestExecutor.getExecutorService().destroy();
 }

 private static void printHello() throws TimeoutException {
  RequestExecutor requestExecutor = RequestExecutor.getExecutorService();
  Future<Boolean> submit = requestExecutor.submit(new Callable<Boolean>() {

   @Override
   public Boolean call() throws Exception {
    System.out.println("Hello");
    return true;
   }
  });

  try {
   submit.get(1, TimeUnit.SECONDS);
  } catch (InterruptedException e) {
   Thread.currentThread().interrupt();
  } catch (ExecutionException e) {
   System.err.println("error:" + e.getStackTrace());
  }
 }
}

 

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.