Java – Why Use ExecutorService for Long-Running Threads

designjavamultithreading

I want an object that will spawn a daemon thread that will continue to run for the lifetime of the process. Let's say, just for the sake of the argument, that it's a thread in an embedded system, and it waits to receive and handle commands on some diagnostic port. But, it could be anything really. The main idea is that it's watching something over a long period of time; It's not performing a sequence of tasks.

Common Java wisdom says, Never instantiate Thread, Use an ExecutorService instead. (E.g., see this answer) But what's the benefit? Using a thread pool as a means to create a single long-running thread seems pointless. How would it be any better than if I wrote this?

class Foobar {
    public Foobar() {
        this.threadFactory = Executors.defaultThreadFactory();
        ...
    }

    public Foobar(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        ...
    }

    public void start() {
        fooThread = threadFactory.newThread(new Runnable() { ... });
        fooThread.setDaemon(true);
        fooThread.start();
    }
    ...
}

Note: this question seems similar to mine, but the answers only say how to use a thread pool, not why.

Best Answer

I think that "Never" is too strong a word. It's more like the second rule of optimization: "Don't (yet)".

In my opinion, the primary reason to use an executor service is to manage the number of threads running. If you allow arbitrary classes to create their own threads, you may quickly find yourself with 1,000 CPU-bound (or constantly context-switching) threads. An executor service solves that problem, by providing constraints on thread creation.

A secondary reason is that properly starting a thread requires some thought:

  • Daemon or non-daemon? Get this one wrong and you need to call System.exit() to shut down your program.
  • What priority? A lot of Swing programmers thought they were being smart by spinning up background threads to handle CPU-intensive tasks, but didn't realize that the event dispatch thread runs at highest priority and child threads inherit their parent's priority.
  • How to deal with uncaught exceptions?
  • What's an appropriate name? Seems silly to obsess over a name, but purpose-named threads give you a lot of help in debugging.

That said, there are certainly cases where it's appropriate to spin up threads rather than rely on a thread pool.

  • I would use the thread pool in the case where my application is generating tasks and I want to control how those tasks are executed.
  • I would create dedicated threads where one or a few threads is dedicated to processing externally-generated events.

"Externally generated" is context dependent. The classic case is the thread at the end of a message queue or socket, which needs to poll that queue and perform some action (which may involve dispatching a task to a pool).

However, it could also refer to events generated outside a particular module: for example, I consider it perfectly reasonable that Log4J's AsyncAppender spins up its own thread rather than expecting the application to provide a pool.

Related Topic