Thread Local can be considered as a scope of access, like a request scope or session scope. It’s a thread scope. It has following:
- Values stored in Thread Local are global to the thread, meaning that they can be accessed from anywhere inside that thread. If a thread calls methods from several classes, then all the methods can see the Thread Local variable set by other methods (because they are executing in same thread). The value need not be passed explicitly. It’s like how you use global variables.
- Values stored in Thread Local are local to the thread, meaning that each thread will have it’s own Thread Local variable. One thread can not access/modify other thread’s Thread Local variables.
Comparing Thread locals in other languages and Java
A thread-local variable effectively provides a separate copy of its value for each thread that uses it. Each thread can see only the value associated with that thread, and is unaware that other threads may be using or modifying their own copies. Some compilers (such as the Microsoft Visual C++ compiler or the IBM XL FORTRAN compiler) have incorporated support for thread-local variables into the language using a storage-class modifier (like static
or volatile
). Java compilers offer no special language support for thread-local variables; instead, they are implemented with the ThreadLocal
class, which has special support in the core Thread
class.
Because thread-local variables are implemented through a class, rather than as part of the Java language itself, the syntax for using thread-local variables is a bit more clumsy than for language dialects where thread-local variables are built in. To create a thread-local variable, you instantiate an object of class ThreadLocal
. The ThreadLocal
class behaves much like the various Reference
classes in java.lang.ref
; it acts as an indirect handle for storing or retrieving a value.
When to use ThreadLocal?
Consider a case when some application is connecting to database. Now you may provide some DAO with connection object as field:
class MyDao{
Connection con;
public void doSomeBusinessLogic()
{
con = datasource.getConnection();
con.doUpdate();
}
}
public synchronized void doBusinessLogic()
{
Connection con = datasource.getConnection();
//some logic
//do update
con.close();
}
- We have to create connection object each time method is called, which is very expensive.
- Also as threads increase, high processor usage will result. So this may result in problem.
Solution 2 : Make a connection pool and get connections from there.
public synchronized void doBusinessLogic(){
Connection con = pool.getConnection();
//some logic
//do update
}
This approach is fine. But still there is problem of contention. If you are using multi-threaded environment, with multicores, it will be waste of resources to use Connection pooling approach.
Solution 3 : Use thread local
Now we can use ThreadLocal to avoid contention.
Suppose we have thread T1 and T2. ThreadLocal for T1 has a map in which it will have T1 and corresponding connection object. Similarilty ThreadLocal for T2 has T2 and corresponding connection object. Now when doing the same business logic, we can say:
public void doBusinessLogic(){
Connection con = myThreadLocal.connection;
con.doUpdate();
}
So we don't have to create the connection object again and again. Also if hardware is good there is no contention on the database. Suppose later we need some other local variable say transaction, even that will be saved in the map like this.
Some people say that we can pass connection as parameter in the business method, but that will make code look horrible and cumbersome. But that is a solution.
Advantage of ThreadLocal:
- No contention. If memory is not an issue, thread local is the best approach.
- It avoids using local objects like connection to be used as parameter, making code look simpler but still efficient.
Example code
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 as discussed above. 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).
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.
First make the DAO or transaction dealing class:
package com.vaani.dao;
public class SomeDAO {
private String transactionId = null;
//some methods to deal with transaction
public void setTransactionId(String transId)
{
transactionId = transId;
}
public String getTransactionId() {
return transactionId;
}
}
Now create Thread Local class to hold this Dao above.
package com.vaani.threadlocal;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.
import com.vaani.dao.SomeDAO;
public class MyThreadLocal {
public static final ThreadLocal userThreadLocal
= new ThreadLocal();
public static void set(SomeDAO dao) {
userThreadLocal.set(dao);
}
public static void unset() {
userThreadLocal.remove();
}
public static SomeDAO get() {
return (SomeDAO)userThreadLocal.get();
}
}
Let’s create our main class file which will generate and set the transaction ID in thread local and then call the business method.
package com.vaani.demo;
import com.vaani.businesscode.BusinessService;
import com.vaani.dao.SomeDAO;
import com.vaani.threadlocal.MyThreadLocal;
public class ThreadLocalDemo extends Thread {
public static void main(String args[]) {
Thread threadOne = new ThreadLocalDemo();
threadOne.start();
Thread threadTwo = new ThreadLocalDemo();
threadTwo.start();
}
@Override
public void run() {
// sample code to simulate transaction id
SomeDAO dao = new SomeDAO();
dao.setTransactionId(getName());
// set the context object in thread local
// to access it somewhere else
MyThreadLocal.set(dao);
/* note that we are not explicitly
passing the transaction id */
new BusinessService().businessMethod();
MyThreadLocal.unset();
}
}
Finally, here’s the code for the BusinessService.java which will read from thread local and use the value.
package com.vaani.businesscode;
import com.vaani.dao.SomeDAO;
import com.vaani.threadlocal.MyThreadLocal;
public class BusinessService {
public void businessMethod() {
// get the context from thread local
SomeDAO dao = MyThreadLocal.get();
System.out.println(dao.getTransactionId());
}
}
Output
When you run the ThreadLocalDemo file, you’ll get the below output:
Thread-1
Thread-0
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).
Source Code
You can download Source code from here.
No comments:
Post a Comment