43

Apart from the fact that the Executor interface has some advantages over plain threads (management, for example), is there any real internal difference (big performance difference, resource consumption...) between doing:

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(runnable);

And:

Thread thread = new Thread(runnable);
thread.start();

I'm only asking about a single thread here.

xingbin
  • 27,410
  • 9
  • 53
  • 103
Magd Kudama
  • 3,229
  • 2
  • 21
  • 25
  • maybe help u : https://stackoverflow.com/questions/26938210/executorservice-vs-casual-thread-spawner?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Abdo Bmz Apr 28 '18 at 13:03
  • 1
    In my question I ask only about a single thread executor. I understand the advantages when using multiple threads – Magd Kudama Apr 28 '18 at 13:06
  • Will edit question to make it clear – Magd Kudama Apr 28 '18 at 13:12
  • In the second answer: "Even for a single Thread, I prefer to use Executors.newFixedThreadPool(1);". Why is that? Is it just convenience because you're using the ExecutorService? Or is there any other reason? – Magd Kudama Apr 28 '18 at 13:15
  • How many `Runnable` you are executing – xingbin Apr 28 '18 at 13:18
  • Just 1 runnable being executed – Magd Kudama Apr 28 '18 at 13:19
  • If you keep a reference to your `Executors.newFixedThreadPool(1)` you can reuse the Thread and run another Runnable with less effort rather than using a plain Thread since you do not need to write some Queues behind the scene. – Enzokie Apr 28 '18 at 13:24
  • @MagdKudama `single thread` does not mean `single runnbale`, if you get multiple runnables, and want to execute them in a single thread, you should use `Executors.newFixedThreadPool(1)`. – xingbin Apr 28 '18 at 13:36

4 Answers4

30

Executors#newSingleThreadExecutor() creates ThreadPoolExecutor object under the hood,
see the code here: http://www.docjar.com/html/api/java/util/concurrent/Executors.java.html

  133       public static ExecutorService newSingleThreadExecutor() {
  134           return new FinalizableDelegatedExecutorService
  135               (new ThreadPoolExecutor(1, 1,
  136                                       0L, TimeUnit.MILLISECONDS,
  137                                       new LinkedBlockingQueue<Runnable>()));
  138       }

The documentation of ThreadPoolExecutor explains in what situations it gives advantages:

Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.

If all you need is to just run single thread only once in a while (say once an hour), then in terms of performance, using ThreadPoolExecutor may be slower, since you need to instantiate the whole machinery (pool + thread), then throw it away from memory.

But if you want to use this single thread often (say every 15 seconds), then the advantage is that you create the pool and thread only once, keeping it in memory, and use it all the time saving time creating a new thread every now and then (which might be quite expensive, if you want to use it say every 15 seconds or so).

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
krokodilko
  • 35,300
  • 7
  • 55
  • 79
  • 1
    As I was asking about performance differences and whether I was missing something about it, I think this is the right answer. Very good answers generally though. Thanks – Magd Kudama Apr 28 '18 at 14:30
  • 1
    Another advantage of having an `ExecutorService`/`ThreadPoolExecutor` is that it allows you to submit a task and get a `Future`. If you join the `Future`, you ensure there are no uncaught exceptions. With a plain `Thread`, you need to watch out for uncaught exceptions and may want to use `Thread.setUncaughtExceptionHandler`. – cambunctious Jun 08 '20 at 14:11
11

The major difference is in task execution policy.

By creating a Thread instance or subclassing Thread you are basically executing a single task.

Using Executors.newSingleThreadExecutor() on the other hand allows you to submit multiple tasks. Since those tasks are guaranteed not to be executed concurrently, this allows you to exploit the following thread confinement benefits:

  • No synchronization required when accessing objects that are not thread-safe
  • Memory effects of one task are guaranteed to be visible to the next task
Community
  • 1
  • 1
xuesheng
  • 3,396
  • 2
  • 29
  • 38
4

It is an abstraction and those always come at "cost":

  • some (potential) amount of "performance penalty"
  • a reduced amount of "control" ( that is the whole point - you don't need to deal with the low level details, so, if you had to, ... )

The major difference is that the service enables you to submit multiple tasks, whereas the thread can run exactly one Runnable. On the other hand, you have to worry about things such as "shutting down" the service.

A rule of thumb: performance aspects should be close to "ignorable" here. Because of that, you prefer the "more abstract" executor service solution. Because that allows you to separate your concerns from the actual threading. And more importantly: if you ever choose to use a different kind of implementation for that service ... the rest of your code should not need to care about that.

Long story short: abstractions cost, but in this case, you typically prefer the "more abstract" solution. Because in the end, that reduces the complexity of your solution.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
3

If you only get one Runnable to execute, then there is no big difference between them.

Using plain thread may be a little more efficient because creating an ExecutorService such as ThreadPoolExecutor has something to do besides creating a new thread. For example, creating blocking queue, creating policy, though these things are done implicitly.

And you have to shutdown the executor after this runnable has been executed. Otherwise the single thread in this pool will never exit.

xingbin
  • 27,410
  • 9
  • 53
  • 103