Somehow when I try to set value to the InheritableThreadLocal field while creating an object of class that inherites Thread I get strange behavior: my object doesn't have the value I set while invoking constructor when the execution reaches the printing line in run() method. At the same time when I try to run a thread that inherites my class it sees the value I set while doing previos creation.
I tried to run the following code:
public class InheritableThreadLocalExample {
public static void main(String[] args) {
ThreadOne threadOne = new ThreadOne("user-one", "thread-one");
threadOne.start();
ThreadTwo threadTwo = new ThreadTwo("user-two", "thread-two");
threadTwo.start();
}
public static class ThreadOne extends Thread {
protected static final InheritableThreadLocal<String> USERNAME = new InheritableThreadLocal<>();
public ThreadOne(String username, String threadName) {
super(threadName);
USERNAME.set(username);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ". Username: " + USERNAME.get());
}
}
public static class ThreadTwo extends ThreadOne {
public ThreadTwo(String username, String threadName) {
super(username, threadName);
}
}
}
The output is:
thread-two. Username: user-one
thread-one. Username: null
Why is this happening?
The constructor of ThreadOne
is executed by the main thread, therefore, the statement USERNAME.set(username);
inside this constructor sets the value for the main thread. But the actual values of the inheritable thread locals of the main thread were copied in the constructor of Thread
, i.e. the preceding super(threadName);
, already.
Therefore, the first thread inherits the state before the first USERNAME.set(username);
call whereas the second thread inherits the state after that call; the inheritance relationship is irrelevant, as we can demonstrate:
public class InheritableThreadLocalExample {
public static void main(String[] args) {
ThreadOne threadOne = new ThreadOne("user-one", "thread-one");
printValue();
threadOne.start();
Thread threadTwo = new Thread(
InheritableThreadLocalExample::printValue, "thread-two");
// things happening after Thread's constructor are irrelevant:
ThreadOne.USERNAME.set("user-two");
threadTwo.start();
}
static void printValue() {
System.out.println(Thread.currentThread().getName()
+ ". Username: " + ThreadOne.USERNAME.get());
}
public static class ThreadOne extends Thread {
protected static final InheritableThreadLocal<String> USERNAME
= new InheritableThreadLocal<>();
public ThreadOne(String username, String threadName) {
super(threadName);
USERNAME.set(username);
}
@Override
public void run() {
printValue();
}
}
}
main. Username: user-one
thread-one. Username: null
thread-two. Username: user-one
We can easily set the intended values in the main thread without subclasses:
public class InheritableThreadLocalExample {
static final InheritableThreadLocal<String> USERNAME
= new InheritableThreadLocal<>();
public static void main(String[] args) {
USERNAME.set("user-one");
Thread threadOne = new Thread(
InheritableThreadLocalExample::printValue, "thread-one");
USERNAME.set("user-two");
Thread threadTwo = new Thread(
InheritableThreadLocalExample::printValue, "thread-two");
// demonstrate that each thread has its own value
// and main doesn't need a value:
USERNAME.remove();
threadOne.start();
threadTwo.start();
}
static void printValue() {
System.out.println(Thread.currentThread().getName()
+ ". Username: " + USERNAME.get());
}
}
thread-one. Username: user-one
thread-two. Username: user-two