ThreadLocal
ThreadLocal is one of the interesting areas in Threading field.
You can get a lot of material over this topic on internet, so I will not go though that part here.
Even name suggests that it is going to be local to thread, but you can't take it granted.
Before going for ThreadLocal use in your code, think & analyze the requirements properly to confirm its use.
Below I have tried to put one limitation & you have to think about this before going for ThreadLocal in your code-
public class ThreadLocals {
public static void main(String[] args) {
SomeThread st1 = new SomeThread();
Thread t1 = new Thread(st1);
t1.setName("t1");
Thread t2 = new Thread(st1);
t2.setName("t2");
t1.start();
t2.start();
}
}
class SomeThread implements Runnable {
private String common = "Nitin";
private ThreadLocal<HashMap<String, String>> tl = new ThreadLocal<>();
@Override
public void run() {
HashMap<String, String> map = new HashMap<>(); //This line needs to be noted
map.put("name", common);
tl.set(map);
if(common.equals("Nitin") && Thread.currentThread().getName().equals("t1")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print();
common = "Nit";
map.put("name", common);
tl.set(map);
}
public void print() {
System.out.println(common);
System.out.println(tl.get().get("name"));
}
}
Output : Common: Nitin
ThreadLocal: Nitin
Common: Nit
ThreadLocal: Nitin
Explanation : Here String variable 'common' is like normal declaration, so any change in its value by one thread is visible in
other thread also. As shown in above output. But any change in above threadlocal variable, is not visible in other thread.
As you can see both threads gave same value.
So it looks, ThreadLocal will resolve our every problem where we need to safeguard the variables in multithreaded environments.
But lets see below code first -
public class ThreadLocals {
public static void main(String[] args) {
SomeThread st1 = new SomeThread();
Thread t1 = new Thread(st1);
t1.setName("t1");
Thread t2 = new Thread(st1);
t2.setName("t2");
t1.start();
t2.start();
}
}
class SomeThread implements Runnable {
private String common = "Nitin";
private HashMap<String, String> map = new HashMap<>();
private ThreadLocal<HashMap<String, String>> tl = new ThreadLocal<>();
@Override
public void run() {
map.put("name", common);
tl.set(map);
if(common.equals("Nitin") && Thread.currentThread().getName().equals("t1")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print();
common = "Nit";
map.put("name", common);
}
public void print() {
System.out.println("Common: " + common);
System.out.println("ThreadLocal: " + tl.get().get("name"));
}
}
Output: Common: Nitin
ThreadLocal: Nitin
Common: Nit
ThreadLocal: Nit
Oops, I didn't even make any change in threadlocal variable but still it is showing different value in other thread.
Why? Because ThreadLocal is making that variable as local but if it is holding some object then it is not making that object
local to thread. So is the case here too. Till hashmap was in run(), it was, by default , local to thread. So ThreadLocal
variable was able to behave as expected, but sooner we moved hashmap variable outside run(), things went out-of-control for
ThreadLocal too. So check for these points also before you start relying on ThreadLocal for every problem. Some cases you
will find, where you don't need ThreadLocal.
===============================================================================================
One interesting fact, which we generally don't use while using Synchronization in Multithreading i.e. the use of 'String', we generally do synchronization at object or class level. But if I say to synchronize on some string such no requirement to synchronize on object or class, it is like you want to have your own key. Here multiple objects can be synchronized depending on this key.
Below is an example. Suppose you distributed many instances of 'Product', now these instances are coming to you for some processing & you want to know how many instances of each name came to you, so for that you have one hashmap in your Product class to hold this data & I am synchronizing here based on Product name as shown below & you can try to synchronize in other ways to check the difference.
You can get a lot of material over this topic on internet, so I will not go though that part here.
Even name suggests that it is going to be local to thread, but you can't take it granted.
Before going for ThreadLocal use in your code, think & analyze the requirements properly to confirm its use.
Below I have tried to put one limitation & you have to think about this before going for ThreadLocal in your code-
public class ThreadLocals {
public static void main(String[] args) {
SomeThread st1 = new SomeThread();
Thread t1 = new Thread(st1);
t1.setName("t1");
Thread t2 = new Thread(st1);
t2.setName("t2");
t1.start();
t2.start();
}
}
class SomeThread implements Runnable {
private String common = "Nitin";
private ThreadLocal<HashMap<String, String>> tl = new ThreadLocal<>();
@Override
public void run() {
HashMap<String, String> map = new HashMap<>(); //This line needs to be noted
map.put("name", common);
tl.set(map);
if(common.equals("Nitin") && Thread.currentThread().getName().equals("t1")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print();
common = "Nit";
map.put("name", common);
tl.set(map);
}
public void print() {
System.out.println(common);
System.out.println(tl.get().get("name"));
}
}
Output : Common: Nitin
ThreadLocal: Nitin
Common: Nit
ThreadLocal: Nitin
Explanation : Here String variable 'common' is like normal declaration, so any change in its value by one thread is visible in
other thread also. As shown in above output. But any change in above threadlocal variable, is not visible in other thread.
As you can see both threads gave same value.
So it looks, ThreadLocal will resolve our every problem where we need to safeguard the variables in multithreaded environments.
But lets see below code first -
public class ThreadLocals {
public static void main(String[] args) {
SomeThread st1 = new SomeThread();
Thread t1 = new Thread(st1);
t1.setName("t1");
Thread t2 = new Thread(st1);
t2.setName("t2");
t1.start();
t2.start();
}
}
class SomeThread implements Runnable {
private String common = "Nitin";
private HashMap<String, String> map = new HashMap<>();
private ThreadLocal<HashMap<String, String>> tl = new ThreadLocal<>();
@Override
public void run() {
map.put("name", common);
tl.set(map);
if(common.equals("Nitin") && Thread.currentThread().getName().equals("t1")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
print();
common = "Nit";
map.put("name", common);
}
public void print() {
System.out.println("Common: " + common);
System.out.println("ThreadLocal: " + tl.get().get("name"));
}
}
Output: Common: Nitin
ThreadLocal: Nitin
Common: Nit
ThreadLocal: Nit
Oops, I didn't even make any change in threadlocal variable but still it is showing different value in other thread.
Why? Because ThreadLocal is making that variable as local but if it is holding some object then it is not making that object
local to thread. So is the case here too. Till hashmap was in run(), it was, by default , local to thread. So ThreadLocal
variable was able to behave as expected, but sooner we moved hashmap variable outside run(), things went out-of-control for
ThreadLocal too. So check for these points also before you start relying on ThreadLocal for every problem. Some cases you
will find, where you don't need ThreadLocal.
===============================================================================================
One interesting fact, which we generally don't use while using Synchronization in Multithreading i.e. the use of 'String', we generally do synchronization at object or class level. But if I say to synchronize on some string such no requirement to synchronize on object or class, it is like you want to have your own key. Here multiple objects can be synchronized depending on this key.
Below is an example. Suppose you distributed many instances of 'Product', now these instances are coming to you for some processing & you want to know how many instances of each name came to you, so for that you have one hashmap in your Product class to hold this data & I am synchronizing here based on Product name as shown below & you can try to synchronize in other ways to check the difference.
Just put sleep method in synchronized block & you will see the difference clearly.
===============================================================================================
We had 'Future' in Java5 when we had Executor framework introduced having 'Callable'.
Damn that was interesting, finally we were able to get something back as Runnable was rude, not returning anything.
But we had another problem staring at our faces, blocking of the main thread, waiting to get something out of future.
What if Future not returning anything, as it got stuck in some vicious infinite loop. Now your main thread is also stuck.
Other languages like Go & libraries like Guava were providing the relief from this situation & needed the same in Java.
Yuppy, Java8 brought this much awaited relief to make these calls totally async & now it is upto main thread if it wants to wait to get the result from that other thread, we got 'CompletableFuture'. It is not giving you the guarantee to get the results, I say, it gives you the guarantee that you need not to wait for it to complete & if it is done before main thread finishes, it will give the results. Below is one sample, you can check & you will observe that due to 'Future', in the last of main(), you can see the result from 'CompletableFuture', else you have to put sleep block in main() to make main() wait, else main() doesn't care & it will be done without showing the results from 'CompletableFuture'. And 'Future' keeps main() blocked allowing 'CompletableFuture' return the result. Also 'CompletableFuture' provides many other useful methods to explore.
We had 'Future' in Java5 when we had Executor framework introduced having 'Callable'.
Damn that was interesting, finally we were able to get something back as Runnable was rude, not returning anything.
But we had another problem staring at our faces, blocking of the main thread, waiting to get something out of future.
What if Future not returning anything, as it got stuck in some vicious infinite loop. Now your main thread is also stuck.
Other languages like Go & libraries like Guava were providing the relief from this situation & needed the same in Java.
Yuppy, Java8 brought this much awaited relief to make these calls totally async & now it is upto main thread if it wants to wait to get the result from that other thread, we got 'CompletableFuture'. It is not giving you the guarantee to get the results, I say, it gives you the guarantee that you need not to wait for it to complete & if it is done before main thread finishes, it will give the results. Below is one sample, you can check & you will observe that due to 'Future', in the last of main(), you can see the result from 'CompletableFuture', else you have to put sleep block in main() to make main() wait, else main() doesn't care & it will be done without showing the results from 'CompletableFuture'. And 'Future' keeps main() blocked allowing 'CompletableFuture' return the result. Also 'CompletableFuture' provides many other useful methods to explore.
A good video series to watch -