In this article we will discuss about uses and misuses of ThreadLocal.What is ThreadLocal?Why it is introduced in java?Are there any competitors of ThreadLocal?
As the name suggests , it will help a thread to keep some object as it's local. The value stored in a ThreadLocal instance is local to the current running thread and any other method running on the same thread will see the same value.Some people argue that synchronization is a competitor of ThreadLocal.
But both have different use-case.
Use Case Of Synchronization:
Synchronizing a method or block is useful when multiple threads access the shared variable used in a method.But we don't want lost update problem or race condition here.For example we have a method which is updating an account balance and that account is a shared one.That is used by more than one share holder.For this Synchronization is a must.Here we want the account is to be updated but not at the same instant of time.If more than one share holder is accessing the account at same time they should come one by one.And the updated value of one transaction should be clearly visible to the next share holder.If we let them all to do the transaction at the same instant of time, lost update problem will arise.Here Synchronization is the strong candidate to be in use.
Use Case Of ThreadLocal:
1.ThreadLocal is useful to make per thread context value unique.Like if a thread want it's own transaction id,it's own security context,it's own database connection.
2.We can use TheadLocal as a wrapper on any non Threadsafe class to make it Threadsafe.
3.If we want to pass any information from one method to another , without passing it as method argument ,on which the same thread is executing , then ThreadLocal is the suitable candidate for it.
Implementation Of ThreadLocal:
Consider we have a scenario where we have a single task which is executed in a multithreaded environment.Suppose we have two threads and both are performing the same task simultaneously.But it is instructed to first thread to perform the task 10 times and to the second thread to perform the task 20 times.How can we do this by using ThreadLocal.
1.By Overriding initialValue method
public class ThreadLocaleExample1 {
public void startTheTask(){
final ThreadLocal threadsOwnLocalCopy = new ThreadLocal(){
@Override
protected Integer initialValue()
{
return new Integer(0);
}
};
Thread t1 = new Thread() {
public void run() {
while ((Integer)threadsOwnLocalCopy.get()<new Integer(10)) { doTheTask();
Integer previousCount = (Integer) threadsOwnLocalCopy.get();
threadsOwnLocalCopy.set(new Integer(previousCount.intValue() + 1));
}
System.out.println((Integer)threadsOwnLocalCopy.get());
}
};
Thread t2 = new Thread() {
public void run() {
while ((Integer)threadsOwnLocalCopy.get() <new Integer(20))
{ doTheTask();
Integer previousCount = (Integer) threadsOwnLocalCopy.get();
threadsOwnLocalCopy.set(new Integer(previousCount.intValue() + 1));
}
System.out.println((Integer)threadsOwnLocalCopy.get());
}
};
t1.start();
t2.start();
}
public void doTheTask(){
System.out.println("Do some meaningful task here which is required repetitively");
}
public static void main(String[] args) {
ThreadLocaleExample1 threadLocaleExample1=new ThreadLocaleExample1();
threadLocaleExample1.startTheTask();
}
}
As the name suggests , it will help a thread to keep some object as it's local. The value stored in a ThreadLocal instance is local to the current running thread and any other method running on the same thread will see the same value.Some people argue that synchronization is a competitor of ThreadLocal.
But both have different use-case.
Use Case Of Synchronization:
Synchronizing a method or block is useful when multiple threads access the shared variable used in a method.But we don't want lost update problem or race condition here.For example we have a method which is updating an account balance and that account is a shared one.That is used by more than one share holder.For this Synchronization is a must.Here we want the account is to be updated but not at the same instant of time.If more than one share holder is accessing the account at same time they should come one by one.And the updated value of one transaction should be clearly visible to the next share holder.If we let them all to do the transaction at the same instant of time, lost update problem will arise.Here Synchronization is the strong candidate to be in use.
Use Case Of ThreadLocal:
1.ThreadLocal is useful to make per thread context value unique.Like if a thread want it's own transaction id,it's own security context,it's own database connection.
2.We can use TheadLocal as a wrapper on any non Threadsafe class to make it Threadsafe.
3.If we want to pass any information from one method to another , without passing it as method argument ,on which the same thread is executing , then ThreadLocal is the suitable candidate for it.
Implementation Of ThreadLocal:
Consider we have a scenario where we have a single task which is executed in a multithreaded environment.Suppose we have two threads and both are performing the same task simultaneously.But it is instructed to first thread to perform the task 10 times and to the second thread to perform the task 20 times.How can we do this by using ThreadLocal.
1.By Overriding initialValue method
public class ThreadLocaleExample1 {
public void startTheTask(){
final ThreadLocal
@Override
protected Integer initialValue()
{
return new Integer(0);
}
};
Thread t1 = new Thread() {
public void run() {
while ((Integer)threadsOwnLocalCopy.get()<new Integer(10)) { doTheTask();
Integer previousCount = (Integer) threadsOwnLocalCopy.get();
threadsOwnLocalCopy.set(new Integer(previousCount.intValue() + 1));
}
System.out.println((Integer)threadsOwnLocalCopy.get());
}
};
Thread t2 = new Thread() {
public void run() {
threadsOwnLocalCopy.set(new Integer(previousCount.intValue() + 1));
}
System.out.println((Integer)threadsOwnLocalCopy.get());
}
};
t1.start();
t2.start();
}
public void doTheTask(){
System.out.println("Do some meaningful task here which is required repetitively");
}
public static void main(String[] args) {
ThreadLocaleExample1 threadLocaleExample1=new ThreadLocaleExample1();
threadLocaleExample1.startTheTask();
}
}
Here each thread is given a counter to maintain the number of time the task is executed.
Here we take a ThreadLocal and override the initialValue method and return a new Integer with initial value 0.When threadsOwnLocalCopy.get() is called for first time by each thread,the initialValue method is invoked and a new Integer value is returned.
2.By Implementing setter Method:
public class ThreadLocaleExample2 {
public void startcall(){
final ThreadLocal<Integer> threadsOwnLocalCopy = new ThreadLocal<Integer>();
Thread t1 = new Thread() {
public void run() {
threadsOwnLocalCopy.set(new Integer(0));
while ((Integer)threadsOwnLocalCopy.get()<new Integer(10)) {
Integer prevCount = (Integer) threadsOwnLocalCopy.get();
threadsOwnLocalCopy.set(new Integer(prevCount.intValue() + 1));
}
System.out.println((Integer)threadsOwnLocalCopy.get());
}
};
Thread t2 = new Thread() {
public void run() {
threadsOwnLocalCopy.set(new Integer(0));
while ((Integer)threadsOwnLocalCopy.get()<new Integer(20)) {
Integer prevCount = (Integer) threadsOwnLocalCopy.get();
threadsOwnLocalCopy.set(new Integer(prevCount.intValue() + 1));
}
}
};
t1.start();
t2.start();
}
public void doTheTask(){
System.out.println("Do some meaningful task here which is required repetitively");
}
public static void main(String[] args) {
ThreadLocaleExample2 threadLocaleExample=new ThreadLocaleExample2();
threadLocaleExample.startcall();
}
}
In the second approach , we have not overridden the intialValue() method , but here we are using set() method of ThreadLocal to set the local value of the thread in the very first line of the thread execution.
And then we are getting it and incrementing it as usual.
Internal Implementation of ThreadLocal (Open up the Bonnet and see what is inside the cover):
Now let's discuss internal implementation of ThreadLocal. If we open the thread class we will see
/* ThreadLocal values pertaining to this thread. This map is maintain by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
So Thread class contains a reference of ThreadLocal.But ThreadLocal class contains ThreadLocaMap as a field.ThreadLocalMap is a customized hash map suitable only fo maintaining thread local values.The
ThreadLocaMap class is static inner class of ThreadLocal.so we can say ThreadLocal is the main class and ThreadLocaMap is a subclass.
The entries in this hash map(ThreadLocaMap ) extend WeakReference,using the main ref field (that is ThreadLocal object) as the key.when a set or get is called on a ThreadLocal it first find the current thread.Then get the map (ThreadLocalMap) associated with the current thread.it get the map by calling a method of ThreadLocal class and passing the current thread as parameter.The method defination is as follows
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
And use this ThreadLocal as the key to find the value from the obtained map.The method used for this is from ThreadLocalMap class.The method signature as follows
private Entry getEntry(ThreadLocal key) ;
This method return the value that is set by using setter method or initialValue() method as discussed in above paragraph.
Misuse of ThreadLocal:
There are various uses of ThreadLocal but in absence of a little care we can face a lot of disaster.
Follow the below code
public class ThreadLocalExample3 {
private static ThreadLocal
static {
threadsOwnLocalCopy.set(5);
}
public int getCountOfThread() {
return threadsOwnLocalCopy.get();
}
}
Here in above code we have used a ThreadLocal and we initialized ThreadLocal in a static block.
But the above implementation is not correct Since the above static initialization block will execute only once when the first thread references the class ThreadLocalExample3.When the second thread will come in, it will not execute ThreadLocalExample3.set(5) on that thread, therefore ThreadLocalExample3.get() will returns null instead of 0.
Purging of ThreadLocal Object:
Now the question is when the ThreadLocal object will get purged(Garbage Collected)?The main issue is that of memory usage.When we initialize an object through the initialValue() method, it’s easy to forget that we have created it.If that object in turn will create some more object, they all will be available through the life time of the thread.But assuming that the thread is a long running process , then there is a chance that all the objects associated with that thread will be garbage collected only when the thread will die(with some exception) or finish smoothly.Let's consider some cases of it.
1.Thread Pooling
Just assume we have a Thread pool implementation like below.
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new RunnableInstance());
So that our threads will be recycled and will not finish.As it is an encouraging design pattern, most of the real life implementation use Thread pool.In this case the associated ThreadLocal Objects will always be available in memory space whether the thread is in use or not.And this will create memory leak issue .
2.Sevlet Container
One more example for this is Application container which uses Thread Pooling to serve the requests.The thread that serve the servlet request will stay alive in the container till the time the server is not shut down.So all the ThreadLocal referenced object will be available in the container till the container is shut down.
Steps to avoid this:
In the completion of the process never forget to call the ThreadLocal.remove() method.This way when we call ThreadLocal.get() again in the same thread after sometime, the initialValue() method will automatically be called, and the new instance will be created.It also make sense when we use setter of ThreadLocal to initialize a value.So as a rule of thumb, always reset or clean the ThreadLocal after finished with the unit of task, even though it is used in a pooled environment or not.
No comments:
Post a Comment