Learn about ThreadLocal in Java

Tram Ho

1. Introduction

In this article, I will introduce ThreadLocal in Java. It has the ability to store data individually for the current stream – and just encapsulate it in a special type of object.

2. ThreadLocal API

TheadLocal structure allows us to store data that can only be accessed by a particular thread.
Assume that we want an Integer that will be wrapped in a specific Thread like so:

Next, when we want to use this value from a Thread, we just need to call a get() or set() . Simply put, we can think that ThreadLocal stores data inside the map – with the thread being the key.
Therefore, when we call a get() on threadLocalValue , we get an Integer value for the requesting thread:

We can create an instance of ThreadLocal using the static method withInitial() and pass the respective supplier:

To remove the value from the ThreadLocal, we can call the remove() :

To see how to properly use ThreadLocal, we will first look at an example that doesn’t use ThreadLocal, then we’ll rewrite our example to take advantage of that structure.

3. Store User Data in a Map

Consider a program that needs to store data – user-specific Context per user id:

We want one thread per userId. We will create a class SharedMapWithUserContext implements the Runnable interface. The implementation in the run() calls some database through the UserRepository class, UserRepository returns a Context object for a certain userId .
Next, we store that Context in the ConcurentHashMap map with the key is userId :

We can easily test our code by creating and starting 2 threads for 2 different userId and confirming that we have two entries in the userContextPerUserId map:

4. Store the User Data in ThreadLocal

We can rewrite the above example to store the user’s Context using ThreadLocal . Each thread will have its own ThreadLocal instance.
When using ThreadLocal , we need to be very careful because every ThreadLocal instance is associated with a specific thread. In the example, we have a thread that is dedicated to each particular userId and this thread is created by us, so we have full control over it.
The run() will fetch the user context and store it in the ThreadLocal variable using the set() :

We can test it out by starting two threads that will execute the action for a given userId:

After running this code, we will see on the standard output that ThreadLocal has been set for each given thread:

We can see that each user has its own Context .

5. ThreadLocals and Thread Pools

ThreadLocal provides an easy to use API that limits a number of values ​​per thread. This is a sensible way to achieve thread safety in Java. However, we should be extra careful when using ThreadLocals and thread pool together.
To better understand the probable warning, let’s consider the following scenario:

    1. First, the application borrows a thread from the pool.
    1. Then it stores some thread limit value into the current thread’s ThreadLocal.
    1. When the current execution ends, the application returns the thread borrowed to the group.
    1. After a while, the application borrows the same thread to process another request.
    1. Since the application last time did not perform the necessary cleanup, it can reuse the same ThreadLocal data for the new request.

This can have surprising consequences in highly concurrent applications.
One way to get around this problem is to manually delete each ThreadLocal after we are finished using it. Since this approach requires strict code consideration, it can be prone to errors.

5.1. Expand ThreadPoolExecutor

As it turns out, the ThreadPoolExecutor class can be extended and provides a custom hook implementation for the beforeExecute() and afterExecute() . The thread pool will call the beforeExecute() before running anything on the borrowed thread. On the other hand, it will call the afterExecute() after executing our logic.
Therefore, we can extend the ThreadPoolExecutor class and remove the ThreadLocal data in the afterExecute () method:

If we send our request to implement this ExecutorService , then we can be sure that using ThreadLocal and thread pools will not pose security risks to our application.

6. Conclusion

In this article, we looked at the ThreadLocal structure. We have implemented logic that uses ConcurrentHashMap is shared between threads to store the context associated with a particular userId. Next, we rewrite our example to leverage ThreadLocal to store data associated with a particular userId and to a specific thread.
You can refer to how to implement with an example project on github

7. References

An Introduction to ThreadLocal in Java – baeldung.com

Share the news now

Source : Viblo