ThreadLocal study
I have read this great article about java ThreadLocal objects and wanted to test their behaviour.
java.lang.ThreadLocal
This blog post relates to those two Github repositories:
1. ThreadLocal values for threads of a ThreadPoolExecutor:
See this project.
The first Unit Test GlobalCounterTest.java executes 10 thousands commands:
Runnable command = GlobalCounter::increment
It uses a ThreadPoolExecutor with 50 threads.
But the global counter never reaches 10 thousands.
The GlobalCounter.java is the cause of the race condition with the following instruction:
public static void increment() {
value = Integer.valueOf(value.intValue() + 1);
}
Please note that we did not use a ThreadLocal in this first example. The race condition can be fixed with the SafeGlobalCounter.java
Another Unit Test LocalCounterTest.java uses a thread local counter. It executes 10 thousands commands:
Runnable command = LocalCounter::increment;
It uses a ThreadPoolExecutor with 10 threads.
As “the value stored in a ThreadLocal instance is specific (local) to the current running thread”, each thread increments its own counter value.
There is no race condition although we use the same faulty instruction to increment the counter:
public static void increment() {
counter.set(Integer.valueOf(getValue() + 1));
//log.put(Thread.currentThread().getName(),counter.get();
}
The LocalCounter keeps a Map of each thread own counter value. Here is the console output of the LocalCounterTest.java Unit Test.
pool-1-thread-1:2116 pool-1-thread-3:1274 pool-1-thread-2:908 pool-1-thread-5:3526 pool-1-thread-4:322 pool-1-thread-7:294 pool-1-thread-6:1001 pool-1-thread-9:15 pool-1-thread-10:118 pool-1-thread-8:426 10000
2. ThreadLocal values for threads of a Tomcat HTTP Connector pool:
See this project.
HelloControllerIT.java is a SpringBoot Integration Test. We set up the embedded Tomcat thread pool size using the @TestPropertySource annotation. Then we call 1000 times a resource that increments a thread local counter. At the end of the test the total per thread is displayed:
http-nio-auto-1-exec-5:195 http-nio-auto-1-exec-4:198 http-nio-auto-1-exec-1:220 http-nio-auto-1-exec-3:193 http-nio-auto-1-exec-2:194 1000
HelloControllerContextIT.java is another test that requires the use of the ThreadLocal.remove method:
public static void clean() {
context.remove();
}
Indeed we need to clean the value of the ThreadLocal once the request has been processed, because the associated thread lives inside a pool and will be reused for the next request.