What is ThreadLocal?
Disclainer: This article uses JDK 1.8
Let’s take a look at the JDK documentation:
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()} * and remains unchanged on subsequent calls. * <pre> * import java.util.concurrent.atomic.AtomicInteger; * * /
Copy the code
The ThreadLocal class, as its name implies, can be understood as a thread-local variable. That is, if you define a ThreadLocal, each thread that reads or writes to that ThreadLocal is thread isolated and does not affect each other. It provides a mechanism for thread closure by having a separate copy of mutable data for each thread.
ThreadLocal source code analysis
ThreadLocal principle
Ref; :
public class ThreadLocal<T> {
/ / the constructor
public ThreadLocal() {}
// Returns the current thread "initial value" of this thread, a thread-local variable. This method is called the first time it is called
protected T initialValue() {}
// Create a ThreadLocalMap. ThreadLocal is maintained by a map.
void createMap(Thread t, T firstValue) {}
// Returns the thread-local variable value corresponding to the current thread.
public T get() {}
/ / get ThreadLocalMap
ThreadLocalMap getMap(Thread t) {}
// Sets the value of thread-local variables for the current thread
public void set(T value) {}
/** Removes the value of the current thread-local variable to reduce memory usage. In fact, when the thread ends, the local variables of the thread will be garbage collected automatically, so we don't need to call remove, we just call remove to speed up the memory collection. * * /
public void remove() {}
// set the initialValue and call initialValue
private T setInitialValue() {}
// Static inner class, a map to maintain
static class ThreadLocalMap {
// The static inner class of ThreadLocalMap inherits weak references, which is the root cause of no memory leaks
// The Entry key is ThreadLocal and is a weak reference. The value is the value
static class Entry extends WeakReference<ThreadLocal<? >>{... }}... }Copy the code
First, take a look at ThreadLocal’s set() method
public void set(T value) {
// Get the current thread object
Thread t = Thread.currentThread();
// Get ThreadLocalMap here
ThreadLocalMap map = getMap(t);
// If the map exists, store the current thread object t as the key and the object to be stored as the value in the map
if(map ! =null)
map.set(this, value);
else
createMap(t, value);
}
Copy the code
static class ThreadLocalMap {
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. * /
static class Entry extends WeakReference<ThreadLocal<? >>{
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<? > k,Object v) {
super(k); value = v; }}/ /... For a long
}
Copy the code
Notice that ThreadLocalMap is an inner class of ThreadLocal. Use the Entry class for storage
Our values are stored on this Map, and the key is the current ThreadLocal object!
If the Map does not exist, initialize one:
/** Create the map associated with a ThreadLocal. Overridden in InheritableThreadLocal. Params: T -- the current thread firstValue -- value for the initial entry of the map **/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code
If the Map exists, we get it from Thread:
/** Get the map associated with a ThreadLocal. Overridden in InheritableThreadLocal. Params: T -- The current thread Returns: the map **/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Copy the code
Thread maintains the ThreadLocalMap variable:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Copy the code
To sum up, threadLocalMaps are written in ThreadLocal using inner classes, but object references are in Thread.
Thread maintains a Map of the ThreadLocalMap for each Thread. The key of the ThreadLocalMap is the ThreadLocal object itself, and the value is the object to store.
With that in mind, the get() method is not at all difficult to understand:
/** * gets the value of the entry under the current thread. * Fetch value */ from the map using the current ThreadLocal key
public T get() {
// Get the current thread
Thread t = Thread.currentThread();
// Get the ThreadLocalMap object for the current thread.
ThreadLocalMap map = getMap(t);
// If yes. Then the entry object under this ThreadLocalMap is obtained. If the entry is also obtained, the value corresponding to the entry can be directly obtained and returned.
if(map ! =null) {
// Get the entry object under this ThreadLocalMap
ThreadLocalMap.Entry e = map.getEntry(this);
// If entry is also obtained
if(e ! =null) {
@SuppressWarnings("unchecked")
// The value corresponding to the entry is returned.
T result = (T)e.value;
returnresult; }}// If ThreadLocalMap or Entry is not obtained, set the initial value.
// The initial value method is lazy loading, which is only used in get, and is only initialized if the fetch fails
return setInitialValue();
}
Copy the code
conclusion
- Each Thread maintains a reference to a ThreadLocalMap
- ThreadLocalMap is an internal class of ThreadLocal that uses Entry for storage
- When you call ThreadLocal’s set() method, you actually set a value to the ThreadLocalMap, where the key is the ThreadLocal object and the value is the object passed in
- When you call ThreadLocal’s get() method, you actually fetch the value from ThreadLocalMap, and the key is the ThreadLocal object
- ThreadLocal itself does not store values; it simply acts as a key for the thread to retrieve values from ThreadLocalMap.
So ThreadLocal can implement data isolation, fetching local variable values from the current thread, independent of other threads.
Are objects in ThreadLocal thread-safe?
If a threadlocale.set () enters a shared object in each thread, such as a static object, then threadlocale.get () will fetch the shared object itself, or it will be unsafe for concurrent access.
What about ThreadLocal memory leaks in thread pools?
The problem
First analysis:
ThreadLocalMap.Entry
Will the key leak memory?ThreadLocalMap.Entry
Does the value of the
Take a look at the core source code of key-value
static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}Copy the code
The super(key) class is a weak reference, so there is no memory leak for the key because it is not a strong reference and can be collected by GC.
Weak reference characteristics: If the object is only associated with weak references and there is no strong reference association, then the object can be collected by GC. Weak references do not prevent GC collection. This is JVM knowledge.
Value is a strong reference, but it is not a problem because the thread terminates. Both strong and weak references are discarded by GC because the reference chain is broken.
There seems to be nothing wrong with this analysis, but what about thread pools? Once created, the core thread will be reused and its life cycle will not end. However, weak references to keys will be recycled by GC, and strong references to values will not be recycled, thus forming the following scene:
Thread - > ThreadLocalMap - > Entry (the key is null) - > value
Since value and Thread are still connected and reachable, they will not be recycled. As a result, more and more garbage objects are generated but cannot be recycled. Sooner or later, memory leaks will occur, and after a long time, they must be OOM.
So the solution ThreadLocal has already figured out for us, providing the remove() method, which removes the value. So remember remove() when you’re done.
Avoid Memory leaks
The root cause of a ThreadLocal memory leak is that since ThreadLocalMap has the same lifetime as Thread, memory leaks occur if the corresponding key is not manually removed, not because of weak references. To avoid memory leaks, remove() manually!
Usage scenarios of ThreadLocal
The most common uses of ThreadLocal are for database connections, Session management, and so on.
Database connection:
private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>() {
public Connection initialValue() {
returnDriverManager.getConnection(DB_URL); }}; publicstatic Connection getConnection() {
return connectionHolder.get();
}
Copy the code
Session management:
private static final ThreadLocal threadSession =new ThreadLocal();
public static Session getSession()throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s ==null) { s = getSessionFactory().openSession(); threadSession.set(s); }}catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
Copy the code
The statement
Ps: I mixed a programmer, if there is an error in the article please inform us, in addition to this article only for learning, reference the following article please inform me to delete: ThreadLocal- Zhihu