private void createQueueProcessor() {
        actionQueue = new LinkedBlockingQueue<Timer>();
        queueProcessor = Executors.newSingleThreadExecutor(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable target) {
                final Thread thread = new Thread(target);
                switch (shutdownType) {
                case TERMINATE_IMMEDIATELY:
                    thread.setDaemon(true);
                    break;

                case TERMINATE_AFTER_COMPLETION:
                    thread.setDaemon(true);
                    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

                        @Override
                        public void run() {
                            // System.out.printf("In shutdown hook.  %d remaining in queue\n", actionQueue.size());
                            ActionChain.this.shutdown();
                            try { thread.join(); } catch (InterruptedException ignore) { }
                        }
                    }));
                    break;

                case TERMINATE_MANUALLY: // Included for completeness.
                default:
                    break;
                }
                return thread;
            }
        });
        queueProcessor.execute(new Runnable() {

            @Override
            public void run() {
                while (true) {
                    Timer timer;
                    try {
                        timer = actionQueue.take();
                        if (timer instanceof TimerShell) {
                            // Shut down queue processor if TimerShell is placed
                            // on queue.
                            queueProcessor.shutdownNow();
                            return;
                        }
                    } catch (InterruptedException e) {
                        Thread.interrupted();
                        return;
                    }
                    // Place the instance in another variable to prevent it
                    // being cleared in between checking and using.
                    TimerAction currentRoot = rootAction;
                    if (currentRoot != null) {
                        currentRoot.timerComplete(timer);
                    }
                }
            }
        });
    }