@Override
 public void run() {
   try {
     boolean running = true;
     while (running) {
       try {
         // block on event availability
         ThreadBoundEvent event = queue.take();
         // add to the batch, and see if we can add more
         batch.add(event);
         if (maxBatchSize > 0) {
           queue.drainTo(batch, maxBatchSize);
         }
         // check for the stop condition (and remove it)
         // treat batches of 1 (the most common case) specially
         if (batch.size() > 1) {
           ListIterator<ThreadBoundEvent> itr = batch.listIterator();
           while (itr.hasNext()) {
             ThreadBoundEvent next = itr.next();
             if (next.getClass().equals(ShutdownTask.class)) {
               running = false;
               ((ShutdownTask) next).latch.countDown();
               itr.remove();
             }
           }
           eventProcessor.process(batch);
         } else {
           // just the one event, no need to iterate
           if (event.getClass().equals(ShutdownTask.class)) {
             running = false;
             ((ShutdownTask) event).latch.countDown();
           } else {
             eventProcessor.process(batch);
           }
         }
       } catch (InterruptedException e) {
         LOG.warn(
             String.format(
                 "Consumer on queue %s interrupted.", Thread.currentThread().getName()));
         // ignore
       } catch (Throwable exception) {
         LOG.error(
             String.format(
                 "exception on queue %s while executing events",
                 Thread.currentThread().getName()),
             exception);
       } finally {
         // reset the batch
         batch.clear();
       }
     }
   } catch (Throwable unexpectedThrowable) {
     // we observed some cases where trying to log the inner exception threw an error
     // don't use the logger here as that seems to be causing the problem in the first place
     System.err.println("Caught and unexpected Throwable while logging");
     System.err.println(
         "This problem happens when jar files change at runtime, JVM might be UNSTABLE");
     unexpectedThrowable.printStackTrace(System.err);
   }
 }
 /**
  * Schedule a runnable for execution.
  *
  * @param event The runnable to execute
  */
 public void execute(ThreadBoundEvent event) {
   if (shuttingDown.get()) {
     throw new RejectedExecutionException("The system is shutting down.");
   }
   int bucket = getBucket(event.getKey());
   BlockingQueue<ThreadBoundEvent> queue = queues.get(bucket);
   queue.add(event);
 }