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 topublic 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