Java Thread Local Storage explained in easiest practical way

Java Thread Local Storage

One of the cool feature of Java that I like is ThreadLocal storage. ThreadLocal storage is explained in easiest way with practical example in this blog.

ThreadLocal is a class in java.lang package which provides a way to store and access data of any type per thread, which means each thread will have it's separate instance of the data. During execution of a thread, one class can set data in ThreadLocal and other classes further down in the execution layer can access/read the data, as long as execution in within same thread. It is important to know that ThreadLocal data set by one thread is not visible or accessible in another thread. That the core concept of ThreadLocal storage.

The way to use ThreadLocal is declare a static property of type ThreadLocal where T is type of the data we want to store. It's static so that it easily accessible in classes wherever needed. You can declare it public, private or whatever make sense in your usecase. Usually declaring it as public make sense.

Since the way ThreadLocal storage works, it must be used carefully. To avoid incorrect use of ThreadLocal by mistake, it is good idea to declare ThreadLocal variables in a separate class and name the class appropriate to call it out load.

package com.readtorakesh.threadlocal;
public class AppThreadLocal {
public static ThreadLocal<UserContext> USER_CONTEXT = new ThreadLocal<>();
}
Once declared you can set data into ThreadLocal variable
AppThreadLocal.USER_CONTEXT.set(userContext);
And to read data from ThreadLocal variable
UserContext userContext = AppThreadLocal.USER_CONTEXT.get();
Lets see the practical use of ThreadLocal variable. Use case is in practical when we write service API, we need the UserContext (calling user's userId, name, may roles as well) available in all layer's (API, Service, Facade, DAO etc). One way to make UserContext available in all these layers is to pass UserContext as parameter in all methods starting from top to the bottom most layer, which sounds like over kill and cluttering you method signature. ThreadLocal is best fit in such situation. At the first layer we can put UserContext in a public static ThreadLocal variable, access and it in all layer's further down during execution, once execution is done, remove it from ThreadLocal.

Lets see how does the code look like to implement this.

UserContext is the class whose instance we want to store in ThreadLocal Storage
package com.readtorakesh.threadlocal;
public class UserContext {
private String userId;
private String firstName;
private String lastName;
public UserContext(String userId, String firstName, String lastName) {
super();
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "UserContext [userId=" + userId + ", firstName=" + firstName + ", lastName=" + lastName + "]";
}
}

The class where we defined ThreadLocal variable
package com.readtorakesh.threadlocal;
public class AppThreadLocal {
public static ThreadLocal<UserContext> USER_CONTEXT = new ThreadLocal<>();
}

The Thread which sets UserContext in ThreadLocal variable, calls service api (where UserContext will be read from ThreadLocal) and once done remove data from ThreadLocal variable.
package com.readtorakesh.threadlocal;
public class RequestorThread extends Thread {
private UserContext userContext;
public RequestorThread(UserContext userContext) {
super(userContext.getUserId()+"-Thread");
this.userContext = userContext;
}
@Override
public void run() {
// Setting userContext in Thread Local here, this will be available in ServiceApi class
AppThreadLocal.USER_CONTEXT.set(userContext);
new ServiceApi().processRequest();
// Very important remove thread local variable once thread execution is done
AppThreadLocal.USER_CONTEXT.remove();
}
}

Its the service class which reads UserContext from ThreadLocal and just print it. If this calls further calls Facade or DAO, UserContext can be read there in exact same way.
package com.readtorakesh.threadlocal;
public class ServiceApi {
public void processRequest() {
//Reading user context from Thread Local variable, which was set the in the calling thread
UserContext userContext = AppThreadLocal.USER_CONTEXT.get();
System.out.println("[" + Thread.currentThread().getName() + "] : Processing request from user: " + userContext);
}
}
view raw ServiceApi.java hosted with ❤ by GitHub

Finaly the main program. It creates 2 different UserContext instances, creates two threads and pass each UserContext to them and start both threads.
package com.readtorakesh.threadlocal;
public class MainApp {
public static void main(String[] args) {
UserContext userTommy = new UserContext("tommy", "Tom", "Tom");
UserContext userJerry = new UserContext("jerry", "Jeryy", "Jam");
Thread t1 = new RequestorThread(userTommy);
Thread t2 = new RequestorThread(userJerry);
t1.start();
t2.start();
try {
// We want to wait for both t1 and t2 threads to finish
t1.join();
t2.join();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
view raw MainApp.java hosted with ❤ by GitHub

In the output you can see that service method executed by tommy-Thread is getting Tom's UserContext and service method executed by jerry-Thread is getting Jerry's UserContext.

----- OUTPUT ----
[tommy-Thread] : Processing request from user: UserContext [userId=tommy, firstName=Tom, lastName=Tom]
[jerry-Thread] : Processing request from user: UserContext [userId=jerry, firstName=Jeryy, lastName=Jam]
view raw output hosted with ❤ by GitHub
 

Note - 

It is important to remove data from ThreadLocal specially in case if your code is running under a managed container like Application Server. Because containers usually implement thread pool to process request, after processing the thread is release back to thread pool and will be used to processed other upcoming requests. Which means data set in ThreadLocal variable by first request will be available to other requests processed by same thread. Which is not correct. Also if the data put into ThreadLocal is big in size, it may lead to memory leak because thread will always remain alive in the pool and data object in ThreadLocal plus any other objects referenced by it will never become eligible for garbage collection.

The power comes will responsibility so always use ThreadLocal responsibly to avoid trouble.

Download Code

https://github.com/rakeshprajapati1982/thread-local

It you found this blog helpful please share. Any question or comments are always welcome.



Comments

Other Popular Posts

Lambda Expression v/s Anonymous Inner Class - Java 8

EhCache3 In Memory Caching for Performance Improvement

EhCache3 as JCache (JSR-107) Implementation with Cache Statistics

How to enable JPA eclipselink logging in WAS Liberty