ThreadLocal is an important concept in Core Java and associated with Multithreading.
Why use ThreadLocal ?
When you create multiple threads , there are chances of objects getting shared. There is a problem of thread interference and memory inconsistency if an object is shared , modified and accessed by multiple threads. The solution to this is synchronization. There is one more way to avoid it , is use of ThreadLocal , where the ThreadLocal data associated with the running thread cannot be accessed or modified from other threads. As the name suggest , it will be local to the thread.
Let us understand the internal data-structure of ThreadLocal.
The data stored in ThreadLocal is backed with map.ThreadLocal class has a static inner class ThreadLocalMap which again has Entry as a static inner class.
When we use the set method of ThreadLocal it uses ThreadLocalMap to store data.
When we use the get method of ThreadLocal it uses ThreadLocalMap.Entry to display the data.
Demo1:
In the second demo , we will use ThreadLocal object and see how it works.
Demo 2:
In the above example , Task1 and Task2 are using the same static ThreadLocal<String> object. the ThreadLocal class ineternally uses Map and the current threads data will be local to it and not avaialble to other thread.
Conclusion.
ThreadLocal class is using Map implementation internally and the get method uses the current thread as the key.
Why use ThreadLocal ?
When you create multiple threads , there are chances of objects getting shared. There is a problem of thread interference and memory inconsistency if an object is shared , modified and accessed by multiple threads. The solution to this is synchronization. There is one more way to avoid it , is use of ThreadLocal , where the ThreadLocal data associated with the running thread cannot be accessed or modified from other threads. As the name suggest , it will be local to the thread.
Let us understand the internal data-structure of ThreadLocal.
public class ThreadLocal<T> {
..
..
static class ThreadLocalMap {
..
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
..
..
static class ThreadLocalMap {
..
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
The data stored in ThreadLocal is backed with map.ThreadLocal class has a static inner class ThreadLocalMap which again has Entry as a static inner class.
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
When we use the set method of ThreadLocal it uses ThreadLocalMap to store data.
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
When we use the get method of ThreadLocal it uses ThreadLocalMap.Entry to display the data.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
Let us understand the concept of ThreadLocal using a simple example.MyThreadLocal class is having HashMap object which stores data and helps to add , remove and get data.Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
Demo1:
package com.a;
import java.util.HashMap;
import java.util.Map;
public class MyThreadLocal {
private static Map<Thread, Object> myThreadMap = new HashMap<Thread, Object>();
public static void add(Object object) {
myThreadMap.put(Thread.currentThread(), object);
}
public static void remove(Object object) {
myThreadMap.remove(Thread.currentThread());
}
public static Object get() {
return myThreadMap.get(Thread.currentThread());
}
}
MyThreadContext is the class which contains id and name , which we will store in MyThreadLocal.import java.util.HashMap;
import java.util.Map;
public class MyThreadLocal {
private static Map<Thread, Object> myThreadMap = new HashMap<Thread, Object>();
public static void add(Object object) {
myThreadMap.put(Thread.currentThread(), object);
}
public static void remove(Object object) {
myThreadMap.remove(Thread.currentThread());
}
public static Object get() {
return myThreadMap.get(Thread.currentThread());
}
}
package com.a;
public class MyThreadContext {
private int id;
private String name;
public MyThreadContext(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyThreadContext [id=" + id + ", name=" + name + "]";
}
}
We have two Runnable classes MyThread and MyThread2. The Runnable classes are used to create and add the context to the static MyThreadLocal object which is getting shared between two threads.Both the threads are having their individual context data which is added to shared ThreadLocal object. But still each thread get its local context data because the get method is displaying the data on the basis of current thread. The get method is taking the argument of current thread , which works as the key and retrieves the data associated to local thread.public class MyThreadContext {
private int id;
private String name;
public MyThreadContext(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyThreadContext [id=" + id + ", name=" + name + "]";
}
}
package com.a;
class MyThread implements Runnable {
MyThreadContext context = new MyThreadContext(1, "ABC");
@Override
public void run() {
MyThreadLocal.add(context);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "
+ MyThreadLocal.get());
}
}
class MyThread2 implements Runnable {
MyThreadContext context = new MyThreadContext(2, "XYZ");
@Override
public void run() {
MyThreadLocal.add(context);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "
+ MyThreadLocal.get());
}
}
public class MainClass {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread2 mt2 = new MyThread2();
Thread t1 = new Thread(mt, "firstThread");
Thread t2 = new Thread(mt2, "secondThread");
t1.start();
t2.start();
}
}
class MyThread implements Runnable {
MyThreadContext context = new MyThreadContext(1, "ABC");
@Override
public void run() {
MyThreadLocal.add(context);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "
+ MyThreadLocal.get());
}
}
class MyThread2 implements Runnable {
MyThreadContext context = new MyThreadContext(2, "XYZ");
@Override
public void run() {
MyThreadLocal.add(context);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "
+ MyThreadLocal.get());
}
}
public class MainClass {
public static void main(String[] args) {
MyThread mt = new MyThread();
MyThread2 mt2 = new MyThread2();
Thread t1 = new Thread(mt, "firstThread");
Thread t2 = new Thread(mt2, "secondThread");
t1.start();
t2.start();
}
}
In the second demo , we will use ThreadLocal object and see how it works.
Demo 2:
package com.c;
class Task1 implements Runnable {
ThreadLocal<String> tLocal;
public Task1(ThreadLocal<String> tLocal) {
super();
this.tLocal = tLocal;
}
@Override
public void run() {
tLocal.set("Task1");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(" Task1 transaction is : " + tLocal.get());
}
}
class Task2 implements Runnable {
ThreadLocal<String> tLocal;
public Task2(ThreadLocal<String> tLocal) {
super();
this.tLocal = tLocal;
}
@Override
public void run() {
tLocal.set("Task2");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(" Task2 transaction is : " + tLocal.get());
}
}
public class ThreadLocalDemo {
private static ThreadLocal<String> tLocal = new ThreadLocal<String>();
public static void main(String[] args) {
Task1 task1 = new Task1(tLocal);
Task2 task2 = new Task2(tLocal);
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
t1.start();
t2.start();
}
}
class Task1 implements Runnable {
ThreadLocal<String> tLocal;
public Task1(ThreadLocal<String> tLocal) {
super();
this.tLocal = tLocal;
}
@Override
public void run() {
tLocal.set("Task1");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(" Task1 transaction is : " + tLocal.get());
}
}
class Task2 implements Runnable {
ThreadLocal<String> tLocal;
public Task2(ThreadLocal<String> tLocal) {
super();
this.tLocal = tLocal;
}
@Override
public void run() {
tLocal.set("Task2");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(" Task2 transaction is : " + tLocal.get());
}
}
public class ThreadLocalDemo {
private static ThreadLocal<String> tLocal = new ThreadLocal<String>();
public static void main(String[] args) {
Task1 task1 = new Task1(tLocal);
Task2 task2 = new Task2(tLocal);
Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);
t1.start();
t2.start();
}
}
In the above example , Task1 and Task2 are using the same static ThreadLocal<String> object. the ThreadLocal class ineternally uses Map and the current threads data will be local to it and not avaialble to other thread.
Conclusion.
ThreadLocal class is using Map implementation internally and the get method uses the current thread as the key.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
and each thread has its own data associated with it.Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);