Java ThreadLocal
本文最后更新于:2022年9月13日 下午
The Java ThreadLocal class enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to the same ThreadLocal
variable, the two threads cannot see each other’s ThreadLocal
variables. Thus, the Java ThreadLocal class provides a simple way to make code thread safe that would not otherwise be so.
Java ThreadLocal Tutorial Video
If you prefer video, I have a video version of this Java ThreadLocal tutorial here:
Java ThreadLocal Tutorial
Creating a ThreadLocal
You create a ThreadLocal
instance just like you create any other Java object - via the new
operator. Here is an example that shows how to create a ThreadLocal
variable:
private ThreadLocal threadLocal = new ThreadLocal();
This only needs to be done once per thread. Multiple threads can now get and set values inside this ThreadLocal
, and each thread will only see the value it set itself.
Set ThreadLocal Value
Once a ThreadLocal
has been created you can set the value to be stored in it using its set()
method.
threadLocal.set(“A thread local value”);
Get ThreadLocal Value
You read the value stored in a ThreadLocal
using its get()
method. Here is an example obtaining the value stored inside a Java ThreadLocal
:
String threadLocalValue = (String) threadLocal.get();
Remove ThreadLocal Value
It is possible to remove a value set in a ThreadLocal variable. You remove a value by calling the ThreadLocal
remove()
method. Here is an example of removing the value set on a Java ThreadLocal
:
threadLocal.remove();
Generic ThreadLocal
You can create a ThreadLocal
with a generic type. Using a generic type only objects of the generic type can be set as value on the ThreadLocal
. Additionally, you do not have to typecast the value returned by get()
. Here is a generic ThreadLocal
example:
private ThreadLocal
Now you can only store strings in the ThreadLocal
instance. Additionally, you do not need to typecast the value obtained from the ThreadLocal
:
myThreadLocal.set(“Hello ThreadLocal”);
String threadLocalValue = myThreadLocal.get();
Initial ThreadLocal Value
It is possible to set an initial value for a Java ThreadLocal
which will get used the first time get()
is called - before set()
has been called with a new value. You have two options for specifying an initial value for a ThreadLocal:
- Create a ThreadLocal subclass that overrides the
initialValue()
method. - Create a ThreadLocal with a
Supplier
interface implementation.
I will show you both options in the following sections.
Override initialValue()
The first way to specify an initial value for a Java ThreadLocal
variable is to create a subclass of ThreadLocal
which overrides its initialValue()
method. The easiest way to create a subclass of ThreadLocal
is to simply create an anonymous subclass, right where you create the ThreadLocal
variable. Here is an example of creating an anonymous subclass of ThreadLocal
which overrides the initialValue()
method:
private ThreadLocal myThreadLocal = new ThreadLocal
@Override protected String initialValue() {
return String.valueOf(System.currentTimeMillis());
}
};
Note, that different threads will still see different initial values. Each thread will create its own initial value. Only if you return the exact same object from the initialValue()
method, will all threads see the same object. However, the whole point of using a ThreadLocal
in the first place is to avoid the different threads seeing the same instance.
Provide a Supplier Implementation
The second method for specifying an initial value for a Java ThreadLocal
variable is to use its static factory method withInitial(Supplier)
passing a Supplier
interface implementation as parameter. This Supplier
implementation supplies the initial value for the ThreadLocal
. Here is an example of creating a ThreadLocal
using its withInitial()
static factory method, passing a simple Supplier
implementation as parameter:
ThreadLocal
@Override
public String get() {
return String.valueOf(System.currentTimeMillis());
}
});
Since Supplier
is a functional interface, it an be implemented using a Java Lambda Expression. Here is how providing a Supplier
implementation as a lambda expression to withInitial()
looks:
ThreadLocal threadLocal = ThreadLocal.withInitial(
() -> { return String.valueOf(System.currentTimeMillis()); } );
As you can see, this is somewhat shorter than the previous example. But it can be made even a bit shorter yet, using the most dense syntax of lambda expressions:
ThreadLocal threadLocal3 = ThreadLocal.withInitial(
() -> String.valueOf(System.currentTimeMillis()) );
Lazy Setting of ThreadLocal Value
In some situations you cannot use the standard ways of setting an initial value. For instance, perhaps you need some configuration information which is not available at the time you create the ThreadLocal variable. In that case you can set the initial value lazily. Here is an example of how setting an initial value lazily on a Java ThreadLocal:
public class MyDateFormatter {
private ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<>();
public String format(Date date) {
SimpleDateFormat simpleDateFormat = getThreadLocalSimpleDateFormat();
return simpleDateFormat.format(date);
}
private SimpleDateFormat getThreadLocalSimpleDateFormat() {
SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get();
if(simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
simpleDateFormatThreadLocal.set(simpleDateFormat);
}
return simpleDateFormat;
}
}
Notice how the format()
method calls the getThreadLocalSimpleDateFormat()
method to obtain a Java SimpleDatFormat instance. If a SimpleDateFormat
instance has not been set in the ThreadLocal
, a new SimpleDateFormat
is created and set in the ThreadLocal
variable. Once a thread has set its own SimpleDateFormat
in the ThreadLocal
variable, the same SimpleDateFormat
object is used for that thread going forward. But only for that thread. Each thread creates its own SimpleDateFormat
instance, as they cannot see each others instances set on the ThreadLocal
variable.
The SimpleDateFormat
class is not thread safe, so multiple threads cannot use it at the same time. To solve this problem, the MyDateFormatter
class above creates a SimpleDateFormat
per thread, so each thread calling the format()
method will use its own SimpleDateFormat
instance.
Using a ThreadLocal with a Thread Pool or ExecutorService
If you plan to use a Java ThreadLocal
from inside a task passed to a Java Thread Pool or a Java ExecutorService, keep in mind that you do not have any guarantees which thread will execute your task. However, if all you need is to make sure that each thread uses its own instance of some object, this is not a problem. Then you can use a Java ThreadLocal with a thread pool or ExecutorService
just fine.
Full ThreadLocal Example
Here is a fully runnable Java ThreadLocal
example:
public class ThreadLocalExample {
1 |
|
}
public class MyRunnable implements Runnable {
1 |
|
}
This example creates a single MyRunnable
instance which is passed to two different threads. Both threads execute the run()
method, and thus sets different values on the ThreadLocal
instance. If the access to the set()
call had been synchronized, and it had not been a ThreadLocal
object, the second thread would have overridden the value set by the first thread.
However, since it is a ThreadLocal
object then the two threads cannot see each other’s values. Thus, they set and get different values.
InheritableThreadLocal
The InheritableThreadLocal
class is a subclass of ThreadLocal
. Instead of each thread having its own value inside a ThreadLocal
, the InheritableThreadLocal
grants access to values to a thread and all child threads created by that thread. Here is a full Java InheritableThreadLocal
example:
public class InheritableThreadLocalBasicExample {
1 |
|
}
This example creates a normal Java ThreadLocal and a Java InheritableThreadLocal. Then the example creates one thread which sets the value of the ThreadLocal and InheritableThreadLocal - and then creates a child thread which accesses the values of the ThreadLocal and InheritableThreadLocal. Only the value of the InheritableThreadLocal is visible to the child thread.
Finally the example creates a third thread which also tries to access both the ThreadLocal and InheritableThreadLocal - but which does not see any of the values stored by the first thread.
The output printed from running this example would look like this:
===== Thread 1 =====
Thread 1 - ThreadLocal
Thread 1 - InheritableThreadLocal
===== ChildThread =====
null
Thread 1 - InheritableThreadLocal
===== Thread2 =====
null
null