synchronized void transactionException(TransactionQueueEvent event, Throwable e) {
    mServicingCount--;
    mTotalServiceExceptions++;

    if (mListeners.size() > 0) {
      event = new TransactionQueueEvent(event, e);

      Iterator it = mListeners.iterator();
      while (it.hasNext()) {
        ((TransactionQueueListener) it.next()).transactionException(event);
      }
    }
  }
  synchronized void transactionExpired(TransactionQueueEvent event) {
    mServicingCount--;
    mTotalExpired++;

    if (mListeners.size() > 0) {
      event = new TransactionQueueEvent(event);

      Iterator it = mListeners.iterator();
      while (it.hasNext()) {
        ((TransactionQueueListener) it.next()).transactionExpired(event);
      }
    }
  }
  synchronized void uncaughtException(Throwable e) {
    mTotalUncaughtExceptions++;

    if (mExceptionListeners.size() > 0) {
      UncaughtExceptionEvent event = new UncaughtExceptionEvent(this, e);

      Iterator it = mExceptionListeners.iterator();
      while (it.hasNext()) {
        ((UncaughtExceptionListener) it.next()).uncaughtException(event);
      }
    } else {
      Thread current = Thread.currentThread();
      current.getThreadGroup().uncaughtException(current, e);
    }
  }
  synchronized void transactionServiced(TransactionQueueEvent event) {
    TransactionQueueEvent svcEvent = new TransactionQueueEvent(event);

    mTotalServiceDuration += (svcEvent.getTimestampMillis() - event.getTimestampMillis());

    if (mListeners.size() > 0) {
      Iterator it = mListeners.iterator();
      while (it.hasNext()) {
        ((TransactionQueueListener) it.next()).transactionServiced(svcEvent);
      }
    }

    // Adjust counters at end in case a listener threw an exception and let
    // the call to transactionException adjust the counters instead.
    mServicingCount--;
    mTotalServiced++;
  }
  synchronized TransactionQueueEvent transactionDequeued(TransactionQueueEvent event) {

    if (++mServicingCount > mPeakServicingCount) {
      mPeakServicingCount = mServicingCount;
    }

    TransactionQueueEvent deqEvent = new TransactionQueueEvent(event);

    mTotalQueueDuration += (deqEvent.getTimestampMillis() - event.getTimestampMillis());

    if (mListeners.size() > 0) {
      Iterator it = mListeners.iterator();
      while (it.hasNext()) {
        ((TransactionQueueListener) it.next()).transactionDequeued(deqEvent);
      }
    }

    return deqEvent;
  }
  /**
   * Enqueues a transaction that will be serviced when a worker is available. If the queue is full
   * or cannot accept new transactions, the transaction is not enqueued, and false is returned.
   *
   * @return true if enqueued, false if queue is full or cannot accept new transactions.
   */
  public synchronized boolean enqueue(Transaction transaction) {
    mTotalEnqueueAttempts++;

    if (transaction == null || mThreadPool.isClosed()) {
      return false;
    }

    int queueSize;
    if ((queueSize = mQueue.size()) >= mMaxSize) {
      if (mListeners.size() > 0) {
        TransactionQueueEvent event = new TransactionQueueEvent(this, transaction);

        Iterator it = mListeners.iterator();
        while (it.hasNext()) {
          ((TransactionQueueListener) it.next()).transactionQueueFull(event);
        }
      }
      return false;
    }

    if (!mSuspended) {
      if (!ensureWaitingThread()) {
        return false;
      }
    }

    mTotalEnqueued++;

    TransactionQueueEvent event = new TransactionQueueEvent(this, transaction);

    mQueue.addLast(event);

    if (++queueSize > mPeakQueueSize) {
      mPeakQueueSize = queueSize;
    }

    notify();

    if (mListeners.size() > 0) {
      Iterator it = mListeners.iterator();
      while (it.hasNext()) {
        ((TransactionQueueListener) it.next()).transactionEnqueued(event);
      }
    }

    return true;
  }