Table of Contents
Single Thread Executor
When you want to run a task, you can start a thread or you can use the Executors
to create a single thread to run the task.
Consider this block:
package com.datmt.concurrency.java.executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class D_Executor_Single { public static void main(String[] args) { ExecutorService service = Executors.newSingleThreadExecutor(); service.submit(() -> System.out.println("hello")); } }
when you run the code, the output is what the task does:
However, you can see that the program didn’t stop (the green dot, the stop button in the screenshot). To tell a ExecutorService
to stop, you need to call the shutdown
method.
package com.datmt.concurrency.java.executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class D_Executor_Single { public static void main(String[] args) { ExecutorService service = Executors.newSingleThreadExecutor(); try { service.submit(() -> System.out.println("hello")); } finally { service.shutdown(); } } }
Notice that the shutdown method is called inside the finally block. This makes sure shutdown is always called.
The shutdown method doesn’t terminate running threads. It just stops accepting new task submitted to the pool.
You can submit not one but many tasks to an ExecutorService
and they will be executed in the order they are submitted.
package com.datmt.concurrency.java.executor; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class D_Executor_Single { public static void main(String[] args) { ExecutorService service = Executors.newSingleThreadExecutor(); Runnable task1 = () -> System.out.println("Hey from runnable 1"); Runnable task2 = () -> System.out.println("Hey from runnable 2"); try { service.submit(task1); service.submit(task2); } finally { service.shutdown(); } } }
Output:
Multiple thread pool
If you want to have more than one thread, you can use either newFixedThreadPool
or newCachedThreadPool
.
newFixedThreadPool
takes an integer as a parameter. This is the number of threads in the pool. A thread could be reused to execute other tasks.
newCachedThreadPool
creates new threads as needed. That means when there are tasks submitted to the pool and no thread is available, new threads will be created to handle those tasks.
On the other hand, if a thread in a pool created by newCachedThreadPool
is not used for 60 seconds, it will be terminated.
Execute tasks using newCachedThreadPool
public static void main(String[] args) throws InterruptedException { ExecutorService service = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { service.submit(() -> System.out.println("Running in thread " + Thread.currentThread().getName() )); } }
Execute tasks using newFixedThreadPool
public static void main(String[] args) throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { service.submit(() -> System.out.println("Running in thread " + Thread.currentThread().getName() )); } }
Can you spot the differences when executing the above two code blocks?
The first one, which uses newCachedThreadPool
will terminate after 60 seconds while the second one runs forever.
That’s why it is important to call the shutdown method when you have no other tasks to submit to the pool.
public static void main(String[] args) throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { service.submit(() -> System.out.println("Running in thread " + Thread.currentThread().getName() )); } service.shutdown(); }
shutdown vs shutdownNow
As mentioned above, it is important to call the shutdown
method on the ExecutorService when you don’t expect more tasks to be submitted. The shutdown
method does the following this:
- Shutdowns the executor
- Refuses new tasks to be submitted to the pool
- Allow tasks to be complete, even if they are in waiting state
Now, in comparison to shutdown
, shutdownNow
is a bit more extreme:
- Attempt to shutdown all executing tasks
- Refuses to take new tasks
- Do not let waiting tasks get a chance to get executed
- Returns a list of waiting tasks
Conclusion
In this post, we walked through the usage of ExecutorService interface. It is quite convenient to spawn new threads to execute tasks using ExecutorService
. In fact, it’s the preferred way to creating new Thread and call the start
method.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.