public void shutdown() {
   LOG.info(
       format(
           "shutting down the (LinkedBlockingQueue)ThreadBoundExecutor[%s]",
           threadFactory.toString()));
   if (shuttingDown.compareAndSet(false, true)) {
     final CountDownLatch shuttingDownLatch = new CountDownLatch(queues.size());
     for (BlockingQueue<ThreadBoundEvent> queue : queues) {
       queue.add(new ShutdownTask(shuttingDownLatch));
     }
     try {
       if (!shuttingDownLatch.await(30, TimeUnit.SECONDS)) {
         LOG.error(
             format(
                 "timeout while waiting for (LinkedBlockingQueue)ThreadBoundExecutor[%s] queues to empty",
                 threadFactory.toString()));
       }
     } catch (InterruptedException ignore) {
       // we are shutting down anyway
       LOG.warn(
           format(
               "(LinkedBlockingQueue)ThreadBoundExecutor[%s] shutdown interrupted.",
               threadFactory.toString()));
     }
   }
   LOG.info(
       format(
           "(LinkedBlockingQueue)ThreadBoundExecutor[%s] shut down completed",
           threadFactory.toString()));
 }
 public ThreadBoundExecutorImpl(
     ThreadBoundEventProcessor eventProcessor,
     int maxBatchSize,
     ThreadFactory threadFactory,
     int numberOfThreads) {
   this.threadFactory = threadFactory;
   LOG.info(
       format(
           "Initializing (LinkedBlockingQueue)ThreadBoundExecutor[%s]", threadFactory.toString()));
   for (int i = 0; i < numberOfThreads; i++) {
     BlockingQueue<ThreadBoundEvent> queue = new LinkedBlockingQueue<>();
     Thread t = threadFactory.newThread(new Consumer(queue, eventProcessor, maxBatchSize));
     queues.add(queue);
     t.start();
   }
 }