Thursday, February 3, 2011

What is ThreadLocal in Java?


Why we need Thread Local variable?
Objective of thread local class is to share information across multiple threads where each thread will have independent copy of the variable.Normally in multi threaded programs each thread tries to modify the shared information.If we want exclusive right to that state variable,we introduce synchronization which is expensive.However if our use case involves frequent read operations , less number of modify operations and no interdependence among state variables across different threads,we can use thread local variables.
We use thread local variables when each thread wants to get default initialized copy of the variable and modify it as per their algorithm and use it across.It does not depend what would be the value of state variable in different threads.

How to create Thread Local variable?
All we have to do is to create a subclass of thread local and provide implementation for initial value.Normally we achieve this by creating anonymous inner sub class.Then whenever we want to access/update the value we use corresponding get and set methods.
Lets take one use case in which we span multiple threads and create a thread local variable for the simple date format.Here depending upon the thread name we set date format in thread local variable which is then used by another class to print the current date.
Attached is the code snippet for reference.

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.kunaal.threadLocal;

/**
 * Example stating the use of the thread local variables.
 * Here we are spanning multiple threads which set the date format in the threadlocal variables
 * Then we just output the current data time for various threads.
 * 
 * @author Kunaal A Trehan
 *
 */
public class ThreadLocalExample{
 
 public static void main(String[] args) {
  Runnable runnable=new RunnableImpl();
  Thread thread1=new Thread(runnable,"USA");
  Thread thread2=new Thread(runnable,"UK");
  Thread thread3= new Thread(runnable);
  
  thread1.start();
  thread2.start();
  thread3.start();
 }
}

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.kunaal.threadLocal;

import java.text.SimpleDateFormat;

/**
 * @author Kunaal A Trehan
 *
 */
public class ThreadLocalDataHolder {

 /**
  * Thread local variable for date format
  */
 private static ThreadLocal<SimpleDateFormat> dateFormat=new ThreadLocal<SimpleDateFormat>(){
  public SimpleDateFormat initialValue() {
         return new SimpleDateFormat();
     }
 };

 
 public static void setFormat(String pattern){
  dateFormat.get().applyPattern(pattern);
 }
 
 public static SimpleDateFormat get(){
  return dateFormat.get();
 }

 /**
  * @return the dateFormat
  */
 public static ThreadLocal<SimpleDateFormat> getDateFormat() {
  return dateFormat;
 }

 /**
  * @param dateFormat the dateFormat to set
  */
 public static void setDateFormat(ThreadLocal<SimpleDateFormat> dateFormat) {
  ThreadLocalDataHolder.dateFormat = dateFormat;
 }
}

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.kunaal.threadLocal;

/**
 * @author Kunaal A Trehan
 *
 */
public class RunnableImpl implements Runnable {

 /**
  * Overridden run method implementation for Thread
  * Here depending upon the thread name we put date format in 
  * thread local variable which is then used by Date Printer to print the 
  * current date
  */
 @Override
 public void run() {
  String name = Thread.currentThread().getName();
  
  if(name !=null){
   if(name.equals("USA")){
    ThreadLocalDataHolder.setFormat("yyyy.MMMMM.dd GGG hh:mm aaa" );
   }else if(name.equals("UK")){
    ThreadLocalDataHolder.setFormat("yyyy.MM.dd G 'at' hh:mm:ss z" );
   }
  }
  
  DatePrinter.print();
  //Removing thread local references
  ThreadLocalDataHolder.getDateFormat().remove();
  
 }

}

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.kunaal.threadLocal;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Kunaal A Trehan
 *
 */
public class DatePrinter {
 
 public static void print(){
  SimpleDateFormat dateFormat = ThreadLocalDataHolder.get();
  String data = dateFormat.format(new Date(System.currentTimeMillis()));
  System.out.println("Thread Name -"+ Thread.currentThread() + " Current date is -"+ data);
 }

}


Thread Name -Thread[Thread-0,5,main] Current date is -2/17/11 8:14 PM
Thread Name -Thread[USA,5,main] Current date is -2011.February.17 AD 08:14 PM
Thread Name -Thread[UK,5,main] Current date is -2011.02.17 AD at 08:14:50 GMT+05:30



Where all thread locals are used?
Its widely used in lots of frameworks like Spring ,Hibernate and others for passing particular thread specific information like transaction attributes,connection details,authorization information and others.

Is Thread Local safe to use or does it have some issues?
If thread local variables are not handled properly,it may lead to Class loader memory leak issue.Normally we get errors like this OutOfMemory: PermGen error...............................
Reason being if we don't clear the thread local variables after use and if thread local variables references some object.It will still be there and won't be garbage collected even though thread has finished the execution.Gradually with multiple installations/un-installations or multiple threads being spawned this memory issue will cause application to crash .So we should take proper care of removing the referencing to objects in thread local variables after use.

Hope it makes things clearer about Thread Local.Comments are welcome

2 comments:

  1. ThreadLocal variable in Java can create serious memory leaks if it is used to store instances of classes which is loaded by a classloader other than BootStrapClassLoader. For example, stroing Java API classes on ThreadLocal will not cause memory leak in web application, but application class, which is loaded by web-app classloader will.

    ReplyDelete