code: @Override
      protected void afterExecute(Runnable r, Throwable t) {
  
          super.afterExecute(r, t);
  
          // synchronizing on the pool (and actually all its threads)
          // the synchronization is needed to avoid more than one signal if two or more
          // threads decrement almost together and come to the if with 0 tasks together
          synchronized (this) {
              tasksInProcess.decrementAndGet();
              if (tasksInProcess.intValue() == 0) {
                  synchronizer.signalAll();
              }
          }
      }
docs: After calling super's implementation of this method, the amount of tasks which are currently in process is
  decremented. Finally, if the amount of tasks currently running is zero the synchronizer's signallAll() method is
  invoked, thus anyone awaiting on this instance of ThreadPoolExecutor is released.
  
  @see java.util.concurrent.ThreadPoolExecutor#afterExecute(Runnable, Throwable)