Thread Local can be considered as a scope of access, like a request scope or session scope. It’s a thread scope. You can set any object in Thread Local and this object will be global and local to the specific thread which is accessing this object. Global and local!!? Let me explain:
Well, that’s the concept of Thread Local. I hope you understood it (if not, please leave a comment).
We saw what is thread local in the above section. Now let’s talk about the use cases. i.e. when you’ll be needing something like Thread Local.
I can point out one use case where I used thread local. Consider you have a Servlet which calls some business methods. You have a requirement to generate a unique transaction id for each and every request this servlet process and you need to pass this transaction id to the business methods, for logging purpose. One solution would be passing this transaction id as a parameter to all the business methods. But this is not a good solution as the code is redundant and unnecessary.
To solve that, you can use Thread Local. You can generate a transaction id (either in servlet or better in a filter) and set it in the Thread Local. After this, what ever the business method, that this servlet calls, can access the transaction id from the thread local.
This servlet might be servicing more that one request at a time. Since each request is processed in separate thread, the transaction id will be unique to each thread (local) and will be accessible from all over the thread’s execution (global).
Got it!?
Java provides an ThreadLocal object using which you can set/get thread scoped variables. Below is a code example demonstrating what I’d explained above.
Lets first have the Context.java file which will hold the transactionId field.
01.
package
com.veerasundar;
02.
03.
public
class
Context {
04.
05.
private
String transactionId =
null
;
06.
07.
/* getters and setters here */
08.
09.
}
Now create the MyThreadLocal.java file which will act as a container to hold our context object.
01.
package
com.veerasundar;
02.
03.
/**
04.
* this class acts as a container to our thread local variables.
05.
* @author vsundar
06.
*
07.
*/
08.
public
class
MyThreadLocal {
09.
10.
public
static
final
ThreadLocal userThreadLocal =
new
ThreadLocal();
11.
12.
public
static
void
set(Context user) {
13.
userThreadLocal.set(user);
14.
}
15.
16.
public
static
void
unset() {
17.
userThreadLocal.remove();
18.
}
19.
20.
public
static
Context get() {
21.
return
userThreadLocal.get();
22.
}
23.
}
In the above code, you are creating a ThreadLocal object as a static field which can be used by rest of the code to set/get thread local variables.
Let’s create our main class file which will generate and set the transaction ID in thread local and then call the business method.
01.
package
com.veerasundar;
02.
03.
public
class
ThreadLocalDemo
extends
Thread {
04.
05.
public
static
void
main(String args[]) {
06.
07.
Thread threadOne =
new
ThreadLocalDemo();
08.
threadOne.start();
09.
10.
Thread threadTwo =
new
ThreadLocalDemo();
11.
threadTwo.start();
12.
}
13.
14.
@Override
15.
public
void
run() {
16.
// sample code to simulate transaction id
17.
Context context =
new
Context();
18.
context.setTransactionId(getName());
19.
20.
// set the context object in thread local to access it somewhere else
21.
MyThreadLocal.set(context);
22.
23.
/* note that we are not explicitly passing the transaction id */
24.
new
BusinessService().businessMethod();
25.
MyThreadLocal.unset();
26.
27.
}
28.
}
Finally, here’s the code for the BusinessService.java which will read from thread local and use the value.
01.
package
com.veerasundar;
02.
03.
public
class
BusinessService {
04.
05.
public
void
businessMethod() {
06.
// get the context from thread local
07.
Context context = MyThreadLocal.get();
08.
System.out.println(context.getTransactionId());
09.
}
10.
}
When you run the ThreadLocalDemo file, you’ll get the below output:
1.
Thread-0
2.
Thread-1
As you might see, even though we are not explicitly passing the transaction id, the value can be accessed from the business method and printed on the console. Adding to it, the transaction ID differs for each thread (0 and 1).
Well, that’s it. I hope I’d explained it in a simple possible way. Please let me know what do you think about this article in comments. Do leave a comment if you want to add anything to this topic.