This post demonstrates how to use Java ExecutorService and AutoCloseable with try-with-resources. With ExecutorService, we need to call the shutdown method all the time. However, call the shutdown method means we are writing more codes than we should. There has to be a way to run implicitly! We can do so by wrapping our codes with a class that implements the AutoCloseable interface. Does it sound interesting? Read on.
Requirements
We used the following stuff for this post.
- Java 7 or later (JDK)
- Eclipse Mars
- Intellij IDEA
Before Java 7 And Without try-with-resource
Before Java 7, we could write our codes with try-catch statements, as shown below. Notice that the codes are verbose for their purpose – we call the shutdown method in the finally clause after checking for null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorServiceDemo { public static void main(String[] args) { ExecutorService service = null; try { service = Executors.newSingleThreadExecutor(); System.out.println("begin"); service.execute(() -> System.out.println("Generic Task 1")); service.execute(() -> { for (int i = 0; i < 3; i++) System.out.println("Printing Task: " + i); }); service.execute(() -> System.out.println("Generic Task 2")); System.out.println("end"); } finally { if (service != null) service.shutdown(); } } } |
Using ExecutorService With try-with-resources And AutoCloseable
With Java 7 and later, we can create similar functionality with fewer codes. First, we need to wrap our ExecutorService in a class that implements the AutoCloseable interface. Then, we use the class in the try-cath-with-resources passing an instance on an ExecutorService.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | import java.util.concurrent.ExecutorService; public class CloseableExecutorService implements AutoCloseable { private ExecutorService service; public CloseableExecutorService(ExecutorService service) { super(); this.service = service; } public void execute(Runnable runnable) { if(service == null) { return; } service.execute(runnable); } @Override public void close() { if(service != null ) { service.shutdown(); } } } |
Therefore in our main class, we write something as shown below. We no longer need the finally clause and explicitly run the shutdown method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.util.concurrent.Executors; public class ExecutorServiceDemo { public static void main(String[] args) { try(CloseableExecutorService service = new CloseableExecutorService(Executors.newSingleThreadExecutor())) { System.out.println("begin"); service.execute(() -> System.out.println("Generic Task 1")); service.execute(() -> { for (int i = 0; i < 3; i++) System.out.println("Printing Task: " + i); }); service.execute(() -> System.out.println("Generic Task 2")); System.out.println("end"); } } } |
When we run our codes, we get the following output, which is similar to the previous codes’ result, which doesn’t use Java 7.
1 2 3 4 5 6 7 | begin Generic Task 1 Printing Task: 0 Printing Task: 1 Printing Task: 2 end Generic Task 2 |
Why did we need to implement the AutoCloseable interface? Well, any object we use with try-with-resources must implement the AutoCloseable interface, and it always calls the AutoCloseable.close method. Since the ExecutorService class does not implement the AutoCloseable interface, we need to wrap it with something that does. Then, we call the ExecutorService.shutdown method within the close method from the AutoCloseable interface. If we don’t implement the AutoCloseable interface, we’ll get compile-time errors.
And that’s how we can use Java ExecutorService and AutoCloseable with try-with-resources.