code: public static ExecutorService newBlockingFixedThreadPoolExecutor(int nThreads, int queueSize) {
          BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(queueSize);
          RejectedExecutionHandler blockingRejectedExecutionHandler = new RejectedExecutionHandler() {
              @Override
              public void rejectedExecution(Runnable task, ThreadPoolExecutor executor) {
                  try {
                      executor.getQueue().put(task);
                  } catch (InterruptedException e) {
                  }
              }
  
          };
  
          return new ThreadPoolExecutor(nThreads, nThreads,
                  0L, TimeUnit.MILLISECONDS, blockingQueue,
                  blockingRejectedExecutionHandler);
      }
docs: Returns an instance of ThreadPoolExecutor using an bounded queue and blocking when the worker queue is full.
  @param nThreads thread pool size
  @param queueSize workers queue size
  @return thread pool executor