## Preface

ThreadLocal is mainly used to store data for the current thread, this data is only accessible by the current thread.

When defining a ThreadLocal, we can also define specific types of objects stored in the ThreadLocal.

 1  ThreadLocal threadLocalValue = new ThreadLocal<>(); 

Above we have defined a ThreadLocal object that stores an Integer.

To store and get the object in ThreadLocal is also very simple, using get() and set().

 1 2  threadLocalValue.set(1); Integer result = threadLocalValue.get(); 

I can think of ThreadLocal as a map, and the current thread is the key in the map.

 1 2 3   public static ThreadLocal withInitial(Supplier supplier) { return new SuppliedThreadLocal<>(supplier); } 

 1  ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 1); 

withInitial requires a Supplier object, which gets its initial value by calling the Supplier’s get() method.

To remove the stored data from the ThreadLocal, call.

 1  threadLocal.remove(); 

I’ll look at the benefits of using ThreadLocal by comparing two examples.

In a real application, we usually need to store different user information for different user requests. Generally we need to build a global Map to store different user information based on different user IDs for easy access later.

## Storing user data in a Map

Let’s see how to use the global Map if we use.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17  public class SharedMapWithUserContext implements Runnable { public static Map userContextPerUserId = new ConcurrentHashMap<>(); private Integer userId; private UserRepository userRepository = new UserRepository(); public SharedMapWithUserContext(int i) { this.userId = i; } @Override public void run() { String userName = userRepository.getUserNameForUserId(userId); userContextPerUserId.put(userId, new Context(userName)); } } 

Here we have defined a static Map to access user information.

To see how it can be used again.

 1 2 3 4 5 6 7 8   @Test public void testWithMap(){ SharedMapWithUserContext firstUser = new SharedMapWithUserContext(1); SharedMapWithUserContext secondUser = new SharedMapWithUserContext(2); new Thread(firstUser).start(); new Thread(secondUser).start(); assertEquals(SharedMapWithUserContext.userContextPerUserId.size(), 2); } 

## Storing user data in ThreadLocal

If we want to use in ThreadLocal we can do this.

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  public class ThreadLocalWithUserContext implements Runnable { private static ThreadLocal userContext = new ThreadLocal<>(); private Integer userId; private UserRepository userRepository = new UserRepository(); public ThreadLocalWithUserContext(int i) { this.userId = i; } @Override public void run() { String userName = userRepository.getUserNameForUserId(userId); userContext.set(new Context(userName)); System.out.println("thread context for given userId: " + userId + " is: " + userContext.get()); } } 

The test code is as follows.

  1 2 3 4 5 6 7 8 9 10 11 12  public class ThreadLocalWithUserContextTest { @Test public void testWithThreadLocal(){ ThreadLocalWithUserContext firstUser = new ThreadLocalWithUserContext(1); ThreadLocalWithUserContext secondUser = new ThreadLocalWithUserContext(2); new Thread(firstUser).start(); new Thread(secondUser).start(); } } 

After running it, we get the following result.

 1 2  thread context for given userId: 1 is: com.javaisland.Context@2eabdvc thread context for given userId: 2 is: com.javaisland.Context@1e9b6cc 

Different user information is stored in different thread contexts.

Note that when we use ThreadLocal, we must be free to control the threads we create. If you are in an ExecutorService environment, it is better not to use ThreadLocal, because in ExecutorService, threads are not controllable.