Downside of the Executor Service with context to thread local
?? Saral Saxena ??????
?11K+ Followers | Linkedin Top Voice || Associate Director || 15+ Years in Java, Microservices, Kafka, Spring Boot, Cloud Technologies (AWS, GCP) | Agile , K8s ,DevOps & CI/CD Expert
The executor service creates a pool of threads that you can submit tasks to. The benefit of this approach is that you don’t have to manage the reusing of threads yourself, but Java will do this for you. This way, the threads are only created once and will be reused if the executor service is in scope.
There are also some downsides with the ExecutorService.
The first downside is that when a thread is blocked, it won’t pick up a next task to run in the meantime. Threads rather block and wait than picking up a new task. This is problematic because we can only have a limited number of threads. These threads take up a relatively small amount of memory, but there is still a hard limit based on the amount of memory available. If you have a lot of small tasks, the changes are that you will be out of memory way before your CPU is fully utilized
The next problem with ExecutorService is that threads are reused. You are probably wondering right now how this can be a good and a bad thing. Reusing threads is good, but there are little bugs that can sneak into your code when you are not careful with reusing threads. One of these problematic use cases is when tasks use thread local variables. When a task sets a thread local variable but forgets to unset it, the thread the task just ran on will be placed back into the pool with that thread local variable. The next task running on this thread will be able to read the thread local variable from the previous task. This is bad because then your thread local variables are leaking into other threads which can cause all kinds of trouble
领英推荐
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static ThreadLocal < String > threadLocal = new
ThreadLocal < > ();
public static void main(String[] args) {
try (ExecutorService executor = Executors.newFixedThreadPool(1)) {
executor.submit(() - > {
threadLocal.set("Task 1 value");
System.out.println("Task 1: " +
threadLocal.get());
});
executor.submit(() - > {
String value = threadLocal.get();
System.out.println("Task 2: " + value);
});
}
}
}
The output of the code example is as follows:
Task 1: Task 1 value
Task 2: Task 1 value
we created two tasks and an executor service that uses a single thread to run these tasks. The first task sets a thread local variable but does not unset it. So, when task two runs on that same thread, it can access the thread local variable from task one which ran first on that thread. You can see this happening in the output where the value for tasks one and two is the same.