Saturday, March 28, 2015

What is ThreadLocal?

Take a look on the following piece of code:

private int var;

public synchronized void changeVar() {
 var++;
}

Using the synchronized block we define that the var is available only for a thread which captures the monitor/lock of the synchronized block. For example, thread-A changes the value of var and leaves the block (releases monitor), after that thread-B captures the monitor and modifies the value of var changed by thread-A, var gets value 2. That’s a normal and expected behavior, but sometimes we need to have our variables with thread visibility scope.

What I mean is having var independently modified by thread-A and thread-B, if the thread-A calls changeVar method 10 times then var will have value 10 only for thread-A, thread-B may have var equals 0 if the thread has not call changeVar method; to do that we may use ThreadLocal (http://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html) instance.

See below an example of using ThreadLocal in java code:


class MyThreadRunnable implements Runnable {

 private Sample sampleInstance;

 public MyThreadRunnable(Sample sample) {
  this.sampleInstance = sample;
 }

 public void run() {
  for (int iter = 0; iter < 15; ++iter) {
   this.sampleInstance.changeThreadLocalVar();
  }
 }
}

class Sample {

 private ThreadLocal<Integer> tlVar;

 public Sample() {
  tlVar = new ThreadLocal<Integer>();
 }

 public void changeThreadLocalVar() {
  if (tlVar.get() == null) {
   tlVar.set(new Integer(0));
  }
  Integer var = tlVar.get();
  var = var + 1;
  tlVar.set(var);
  System.out.println("Var value: " + tlVar.get() + " changed by "
    + Thread.currentThread().getName());
 }
}

Sample sample = new Sample(); 
Thread thA = new Thread(new MyThreadRunnable(sample), "Thread-A");
Thread thB = new Thread(new MyThreadRunnable(sample), "Thread-B");
thA.start();
thB.start();

The output will be

Var value: 1 changed by Thread-A
Var value: 1 changed by Thread-B
Var value: 2 changed by Thread-B
Var value: 3 changed by Thread-B
Var value: 4 changed by Thread-B
Var value: 5 changed by Thread-B
Var value: 6 changed by Thread-B
Var value: 7 changed by Thread-B
Var value: 8 changed by Thread-B
Var value: 9 changed by Thread-B
Var value: 2 changed by Thread-A
Var value: 3 changed by Thread-A
Var value: 10 changed by Thread-B

And so on…
How does ThreadLocal work? Every thread has a hash map, the map keys are ThreadLocal instances, and every key has an associated value: reference to an object captured by the ThreadLocal.

Calling tlVar.set(var); we use tlVar as a key for the current thread map and with the given key we may extract corresponding reference.

You need to be very careful with ThreadLocal; in case of improper use ThreadLocal may bring serious problems into your project.
Example 1 (incorrect!): what is the output of the code below?


class MyThreadRunnable implements Runnable {
 
 private Sample sampleInstance;
 
 public MyThreadRunnable(Sample sample) {
  this.sampleInstance = sample;
 }
 
 public void run() {
  for (int iter = 0 ; iter < 15; ++ iter) {
   sampleInstance.changeThreadLocalVar();
  }
 }
}



class Sample {
 
 private ThreadLocal<Integer> tlVar;
 
 public Sample() {
  tlVar = new ThreadLocal<Integer>();
  tlVar.set(new Integer(0));
 }
 
 public void changeThreadLocalVar() {
  Integer var = tlVar.get();
  var = var + 1;
  tlVar.set(var);   
  System.out.println("Var value: " + tlVar.get() + " changed by " + Thread.currentThread().getName());
 }
}
…………
Sample sample = new Sample(); 
Thread thA = new Thread(new MyThreadRunnable(sample), "Thread-A");
Thread thB = new Thread(new MyThreadRunnable(sample), "Thread-B");
thA.start();
thB.start();    

Hint: You already know that ThreadLocal variable is a variable visible only for a current thread.

Answer:
The output will be:
Exception in thread "Thread-B" Exception in thread "Thread-A" java.lang.NullPointerException

It happened because an instance of Sample was initialized in the ‘main’ thread so the value of tlVar is available only for a ‘main’ thread but not for Thread-A or Thread-B.
Change the Sample constructor to


public Sample() {
 tlVar = new ThreadLocal<Integer>();
 tlVar.set(new Integer(0));     
 System.out.println("Set tlVar value: " + tlVar.get() + " by thread " + Thread.currentThread().getName());
}

And take a look on the output

Set tlVar value: 0 by thread main

However tlVar is not initialized for Thread-A and Thread-B


Example 2 (incorrect!): what is the output of the code below? (from: http://habrahabr.ru/company/maxifier/blog/218313/)


public class X {
 ThreadLocal<Anchor> local = new ThreadLocal<Anchor>();

 class Anchor {
  byte[] data = new byte[1024 * 1024];
 }

 public Anchor getOrCreate() {
  Anchor res = local.get();
  if (res == null) {
   res = new Anchor();
   local.set(res);
  }
  return res;
 }

 public static void doLeakOneMoreInstance() {
  new X().getOrCreate();
 }

 public static void main(String[] args) throws Exception {
  while (true) {
   doLeakOneMoreInstance();
   System.out.println(Runtime.getRuntime().freeMemory() / 1024 / 1024
     + " MB of heap left");
  }
 }
}

Hint: you already know about ThreadLocal map mechanism
Answer: here we are storing Anchor object reference as a value for internal ThreadLocal map (local.set(res)). The owner if that map is thread ‘main’, until ‘main’ thread is alive the map won’t be processed by garbage collector which will cause OutOfMemoryError.

As a workaround we can mark Anchor class as static.
Keep in a mind that every non-static inner class stores the reference to an external class; it may lead to inefficient memory usage and potential problems like in the example above.

Remember that reference to a ThreadLocal variable is used by the thread which stores this reference, until the thread is alive, GC doesn’t care about the objects associated with the thread. Now think about reusable threads (thread pool), can we get a serious memory leak issue using the ThreadLocal variable for the thread from the pool? Sure as shooting!

Lastly, I’d like to give an example of real using of ThreadLocal.
Spring framework has spring security feature which allows us to get the information about current user and provide corresponding permissions. The fundamental object in spring security is SecurityContextHolder, it stores information about user’s credentials and assigns the security context for the given user. “By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution, even if the security context is not explicitly passed around as an argument to those methods. Using a ThreadLocal in this way is quite safe if care is taken to clear the thread after the present principal's request is processed. Of course, Spring Security takes care of this for you automatically so there is no need to worry about it.” (http://docs.spring.io/spring-security/site/docs/3.0.x/reference/technical-overview.html)


No comments:

Post a Comment