There are scopes of variable in Java programming language:

a. Local variable

b. Member variable (aka instance variable)

c. Static variable (aka class variable)

In this post, let’s discuss an interesting type of variable: ThreadLocal and how it differs from the other variable types.


Video: To see the visual walk-through of this post, click below:


What is ThreadLocal Variable?

ThreadLocal Variable as its name indicates, is local to a thread i.e., is visible only to that particular thread. It can’t be accessed by other threads in the application.

Program without ThreadLocal

Let’s learn about ThreadLocal through this practical example. Let’s say we have a sample program like this:

01: public class NoThreadLocalDemo {
02:
03:    public static void main(String[] args) {
04:      
05:       Controller.method1();
06:    }
07: }
01: public class Controller {
02:
03:    public static void method1() {
04:      
05:       System.out.println("Controller.method1() invoked");
06:       Manager.method2();
07:    }  
08: }
01: public class Manager {
02:
03:    public static void method2() {
04:      
05:       System.out.println("Manager.method2() invoked");
06:       DAO.method3();
07:    }  	
08: }
01: public class DAO {
02:
03:    public static void method3() {
04:      
05:       System.out.println("DAO.method3() invoked");
06:    }  
07: }

Below is the workflow of this program:

a. ‘NoThreadLocalDemo’ class has the ‘main()’ method, which is the entry point to the program. This method invokes ‘Controller#method1()’.

b. ‘Controller’ class has ‘method1()’. This method invokes ‘Manager#method2()’.

c. ‘Manager’ class has ‘method2()’. This method invokes ‘DAO#method3()’.

d. ‘DAO’ class has ‘method3()’ which just prints a statement that it has been invoked.

Now, let’s say we want to initialize an integer variable of value ’10’ in ‘NoThreadLocalDemo#main()’ method and access it in ‘DAO#method3()’, how can you do it? There are few options to do it:

1. Pass the integer variable as a parameter in all the 3 methods ‘Controller#method1()’, ‘Manager#method2()’, and ‘DAO#method3()’ and access it in ‘DAO#method3()’.

2. You can also declare the integer variable as an member variable or static variable in the ‘NoThreadLocalDemo’ class and then access it in ‘DAO#method3()’. However, in this option, the integer variable will become a shared variable and can be accessed by multiple threads. If this integer needs to be a thread safe variable, then declaring them as member variable or static variable is not a right approach.

This leaves us with option #1. However, in this option we need to refactor the ‘Controller#method1()’, ‘Manager#method2()’, and ‘DAO#method3()’  methods to pass the integer variable as a parameter. It might be fine here because we need to modify only 3 methods. However in real enterprise applications, object hierarchy will be deeply nested and we might have to end up refactoring multiple methods. This is where ThreadLocal comes in handy.

ThreadLocal Example

Let’s rewrite the above program using the ‘ThreadLocal’.

01: public class ThreadLocalDemo {
02:
03:    public static final ThreadLocal<Integer> threadLocalInteger = new ThreadLocal<Integer>();
04:   
05:    public static void main(String[] args) {
06:      
07:       threadLocalInteger.set(10);
08:       Controller.method1();
09:    }  
10: }
01: public class Controller {
02:
03:    public static void method1() {
04:      
05:       System.out.println("Controller.method1() invoked");
06:       Manager.method2();
07:    }  
08: }
01: public class Manager {
02:
03:    public static void method2() {
04:      
05:       System.out.println("Manager.method2() invoked");
06:       DAO.method3();
07:    }  	
08: }
01: public class DAO {
02:
03:    public static void method3() {
04:      
05:       int value = threadLocalInteger.get().intValue();
06:       ++value;
07:       System.out.println("DAO.method3() int value: " + value);  	
08:    }
09: }

Here is the workflow of this program:

a. ‘ThreadLocalDemo’ class has the ‘main()’ method which is the entry point to the program. In this class in line# 3, we are creating a ‘threadLocalInteger’ variable whose type is an ‘ThreadLocal’ and it can store an ‘Integer’ wrapper object. This variable is initialized to value ’10’ in line# 7. And this method invokes ‘Controller#method1()’.

b. ‘Controller’ class has ‘method1()’. This method invokes ‘Manager#method2()’.

c. ‘Manager’ class has ‘method2()’. This method invokes ‘DAO#method3()’.

d. ‘DAO’ class has ‘method3()’. This method is retrieving ‘threadLocalInteger’, incrementing its value and printing it.

When we executed the above program, we got following output:

Controller.method1() invoked
Manager.method2() invoked
DAO.method3() int value: 11

Here you can notice that integer variable is not passed as parameter in ‘Controller#method1()’, ‘Manager#method2()’, and ‘DAO#method3()’ methods, however, it is still accessed in ‘DAO#method3()’. This is the power of ThreadLocal. The point to be noted here is: ThreadLocal variables are visible only to a specific thread. Let’s study this with a slightly modified version of this example:

01: public class ThreadLocalTwoThreadsDemo {
02:
03:    public static final ThreadLocal<Integer> threadLocalInteger = new ThreadLocal<Integer>();
04:   
05:    private static class Thread1 extends Thread {
06:
07:       @Override
08:       public void run() {
09:               	
10:          threadLocalInteger.set(10);
11:          Controller.method1();     	
12:       }
13:    }
14:
15:    private static class Thread2 extends Thread {
16:
17:       @Override
18:       public void run() {
19:               	
20:           threadLocalInteger.set(100);
21:           Controller.method1();     	
22:       }
23:    }
24:      
25:    public static void main(String[] args) {
26:
27:       new Thread1().start();
28:       new Thread2().start();
29:    }  
30: }
01: public class Controller {
02:   
03:    public static void method1() {
04:      
05:       System.out.println(Thread.currentThread().getName() + " Controller.method1() invoked");
06:       Manager.method2();
07:    }  
08: }
01: public class Manager {
02:
03:    public static void method2() {
04:      
05:       System.out.println(Thread.currentThread().getName() + " Manager.method2() invoked");  	
06:       DAO.method3();
07:    }  	
08: }
01: public class DAO {
02:
03:    public static void method3() {
04:      
05:       int value = threadLocalInteger.get().intValue();
06:       ++value;
07:       System.out.println(Thread.currentThread().getName() + " DAO.method3() invoked, int value: " +value);  	
08:    }  
09: }

Here is the workflow of this program:

a. ‘ThreadLocalTwoThreadsDemo’ class has the ‘main()’ method which is the entry point to the program. In this class, in line# 3, we are creating a ‘threadLocalInteger’ variable. whose type is a ‘ThreadLocal’ and it can store an ‘Integer’ wrapper object. In this class, we are launching two threads ‘Thread1’ and ‘Thread2’ concurrently in line #27 and #28. In ‘Thread1’ we are setting the ‘threadLocalInteger’ to value to be 10 in line #10 and in ‘Thread2’ we are setting the ‘threadLocalInteger’ to value to be 100 in line #20. After that, both threads invoke the ‘Controller#method1()’.

b. ‘Controller’ class has ‘method1()’. This method invokes ‘Manager#method2()’.

c. ‘Manager’ class has ‘method2()’. This method invokes ‘DAO#method3()’.

d. ‘DAO’ class has ‘method3()’. This method is retrieving ‘threadLocalInteger’, incrementing its value and printing it.

When we executed the above program, we got following output:

Thread 1 Controller.method1() invoked
Thread 2 Controller.method1() invoked
Thread 1 Manager.method2() invoked
Thread 2 Manager.method2() invoked
Thread 1 DAO.method3() invoked, int value: 11
Thread 2 DAO.method3() invoked, int value: 101

You can notice that the ‘Thread1’ is printing the value as ’11’, whereas the ‘Thread2’ is printing the value as ‘101’. This clearly shows that each has its own copy of ‘threadLocalInteger’, that’s why ‘Thread1’ and ‘Thread2’ are printing different values. 

Advantages of ThreadLocal 

a. Avoid expensive Refactoring: Suppose you have an application already running in production. Now a new requirement came which demands you to pass a variable from an object in the UI layer to an object in the DAO layer. In this situation you will be mandated to refactor multiple methods/objects to do so. On the other hand if you use the ThreadLocal variable, you don’t have to undergo any risky code refactoring approach.

b. Use it across all methods: Some information needs to be available all throughout the transaction. Example, if you are writing a logging framework and you want to print contextual information such as thread name, transaction id, … in each and every log statement. In such scenarios ThreadLocal will be quite helpful.

Disadvantages of ThreadLocal 

a. Threads Handshaking: Sometimes, in our application, one thread might drop the request to an Executor. And a different thread from the Executor service can pick up this request and process the transaction further. In case, if ThreadLocal variables are used, then these variables will not get automatically propagated to new threads. You have to write code manually to propagate these variables to the new thread. Also you need to write code to manually clean up the ThreadLocal variables from this new thread.

b. Data safety: Since threads are expensive to create, they are pooled and reused across multiple requests. So, in case you are not clearing the ThreadLocal variable after processing the request, then the data will be exposed, when the thread processes the next request. It means one customer data becomes visible to another customer. This can lead to nasty production problems.

c. Hard to Debug: Let’s say you are initializing a ThreadLocal variable at the beginning of your transaction and then trying to access it in the middle of your transaction, then it will become hard to maintain. This is because, you will not know in what other places in the code, this ThreadLocal variable is modified and manipulated.

d. Code Maintainability: This disadvantage is a continuation of point ‘#c. Hard to Debug’. If a variable is local or member or static, it is going to be right in the same method or class. So reading the variable’s data type, how it is initialized, where all it’s manipulated becomes easy. If it’s a ThreadLocal variable, you need to go all over the application code base to figure out how it’s handled.

Conclusion

As they said in the Spiderman movie, with ‘Great power comes Great responsibility’. ThreadLocal is a very powerful programming construct. However, when not used with proper care, it will result in nasty/hard to debug production problems.