Question 1. Difference from Synchronized 2. Which region of the JVM is stored 3. Is it true that only the current thread is visible? 4. Does this cause memory leaks? 5. 7. Are objects in ThreadLocal necessarily thread-safe
An overview,
1. Official terminology
The ThreadLocal class is used to provide local variables within a thread. Making these variables accessible in a multithreaded environment (GET/SET) ensures that variables in each thread are relatively independent of variables in other threads.
2. Plain English
ThreadLocal is a class that creates thread-local variables.
In general, we create member variables that are thread-unsafe. Because it can be modified by multiple threads at the same time, this variable is not independent of each other and is shared by multiple threads. Variables created using ThreadLocal can only be accessed by the current thread and cannot be accessed or modified by other threads. Namely: privatize threads to privatize threads.
2. Application scenarios
Each thread needs a unique object (such as a utility class, typically SimpleDateFormat, which is new every time it is used. It is a waste of performance, and it is not thread-safe to put it directly into a member variable, so it is perfect to manage it with ThreadLocal.) Such as:
public class ThreadLocalTest05 { public static String dateToStr(int millisSeconds) { Date date = new Date(millisSeconds); SimpleDateFormat simpleDateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get(); return simpleDateFormat.format(date); } private static final ExecutorService executorService = Executors.newFixedThreadPool(100); public static void main(String[] args) { for (int i = 0; i < 3000; i++) { int j = i; executorService.execute(() -> { String date = dateToStr(j * 1000); // We can see from the result that the thread is safe, time is not repeated. System.out.println(date); }); } executorService.shutdown(); } } class ThreadSafeFormatter { public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); }}; // Java8 // public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = // threadLocal. withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")); }Copy the code
Create a SimpleDateFormat for each thread. Create a SimpleDateFormat for each thread. Create a SimpleDateFormat for each thread. A request comes in from a thread, which may run through N methods. Assuming that three of your methods are using dateToStr(), you can generate three SimpleDateFormat objects if you just use new, while using ThreadLocal will only generate one object, one per thread.
‘Each thread needs to save global variables (such as user information stored in ThreadLocal after successful login, and then the current thread operation business logic directly get to complete, effectively avoid the trouble of passing parameters back and forth), reduce the code coupling degree at a certain level.
Further elaboration is:
For example, store transaction IDS and other information. Each thread is private. For example, aop logging requires before to record the request ID and end to retrieve the request ID, which is also possible. For example, JDBC connection pooling (a typical ThreadLocal usage)…. And so on…
Iii. Core knowledge
1. Class relationships
Each Thread object holds a member variable of ThreadLocalMap. Each ThreadLocalMap maintains N Entry nodes, namely an Entry array. Each Entry represents a complete object. The key is the ThreadLocal itself, and the value is the generic value of ThreadLocal.
The core source code is as follows
/ / Java. Lang. Thread class hold ThreadLocalMap references in the public class Thread implements Runnable {ThreadLocal. ThreadLocalMap threadLocals = null; } java.lang.ThreadLocal has an internal static class ThreadLocalMap public class ThreadLocal<T> {static class ThreadLocalMap {private Entry[] table; Static class Entry extends WeakReference<ThreadLocal<? Static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}}}Copy the code
2. Class diagram
ThreadLocal memory structure diagram.
3. Main methods
InitialValue: indicates the initialization. Lazy loading in the get method. Get: Gets the value of this thread. If get is not set before it is called, the initialValue method is executed internally to initialize it. Set: Sets a new value for this thread. Remove: Remove the value corresponding to this thread, the best way to prevent memory leaks.
3.1, the initialValue
3.1.1 what do you mean
By definition, initialize some values (generic values). Lazy loading.
3.1.2 Trigger timing
If the set method is not called before the GET method is called, the initialValue will be triggered inside the GET method. That is, if the get method does not get something, the initialValue will be triggered.
3.1.3 Supplementary notes
Typically, this method is called at most once per thread. However, if you have already called remove() and then call get() again, initialValue can be triggered again. It is generally recommended to override this method as an anonymous inner class, otherwise null is returned by default. Such as:
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); }}; Public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = threadLocal. withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));Copy the code
3.1.4, source
// The implementation is provided by subclasses. // Protected means give it to subclasses. protected T initialValue() { return null; }Copy the code
3.2, the get
3.2.1. What do you mean
Gets the value in ThreadLocal under the current thread.
3.2.2, source
/** * gets the value of the entry under the current thread. * Get the ThreadLocalMap of the current thread. Public T get() {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 ! Threadlocalmap. entry e = map.getentry (this); // If entry also gets if (e! + + + + = null) {@suppressWarnings ("unchecked") // Return the value corresponding to the entry directly. T result = (T)e.value; return result; }} // If ThreadLocalMap or Entry is not obtained, set the initial value. // The initial value method is lazy loading, which is used only in get, and is only initialized once. return setInitialValue(); }Copy the code
3.3, the set
3.3.1 What do you mean
In fact, it does the same thing as initialValue, it’s a set value, but it’s called at a different time. Set is a method that you can use if you want, the API is here, you can call set if you want. Very free.
3.3.2 rainfall distribution on 10-12, source
/** * Sets the value of the current thread's thread-local variable * The value of ThreadLocal is actually put into one of the current thread's ThreadLocalMap instances, so it can only be accessed in this thread. */ public void set(T value) {// Public void set(T value) { ThreadLocalMap (ThreadLocalMap) {// Get the ThreadLocalMap instance of the current thread. ThreadLocalMap map = getMap(t); // If the current thread has a corresponding ThreadLocalMap instance, the current ThreadLocal object is stored as the key and value as the value in the Entry of the ThreadLocalMap. if (map ! = null) map.set(this, value); CreateMap (t, value); createMap(t, value); createMap(t, value); }Copy the code
3.4, remove
3.4.1 What do you mean
Removes the value of ThreadLocal from the current thread in order to reduce memory footprint. The main purpose is to prevent memory leaks. Memory leak problems are covered below.
3.4.2, source
/** * removes the value of the current thread-local variable to reduce memory usage. The main purpose is to prevent memory leaks. Memory leak problems are covered below. */ public void remove() {// Get the ThreadLocalMap object of the current thread and remove it. ThreadLocalMap m = getMap(Thread.currentThread()); if (m ! = null) // Remove value m.remove(this) from ThreadLocal; }Copy the code
4, ThreadLocalMap
I just want to point out that this object is owned by Thread. Its reference is in the Thread class, which proves the question: Why does the ThreadLocalMap class have an Entry array instead of an Entry object?
Because your business code can new several ThreadLocal objects, each doing its job. But in one request, in one thread, one ThreadLocalMap, not multiple threadLocalMaps, no matter how many times you new threadLocalMaps, there’s only one ThreadLocalMap in one thread, because again, The ThreadLocalMap reference is in the Thread, so its Entry array holds multiple ThreadLocal objects that you create in a Thread.
The core source code is as follows:
This method is called when you call the threadlocal.get () method, which returns a reference to threadLocals from the current thread. ThreadLocalMap getMap(Thread t) {return t.htreadlocals; } public class Thread implements Runnable { // ThreadLocal.ThreadLocalMap ThreadLocal.ThreadLocalMap threadLocals = null; }Copy the code
Four, complete source code
1, core source code
// Local thread. Thread: indicates a Thread. Public class ThreadLocal<T> {// constructor public ThreadLocal() {} ThreadLocal<Integer> count = new ThreadLocal<>(); // You want Integer value = count.get(); value++; That's an error, because count doesn't have a value, it's null, so you need to override this method to initialize value, which is protected which means it's overwritten for subclasses. // This method is a deferred call that is executed when the thread first calls get. Protected T initialValue() {} // Create a ThreadLocalMap. Void createMap(Thread t, t firstValue) {} void createMap(Thread t, t firstValue) {} ThreadLocalMap getMap(Thread T) {} Public void set(T value) {} public void set(T value) {} // Remove 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, 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 memory leaks. // The Entry key is ThreadLocal and weak references. Static class Entry extends WeakReference<ThreadLocal<? > > {}}}Copy the code
2, the set ()
/** * Sets the value of the current thread's thread-local variable * The value of ThreadLocal is actually put into one of the current thread's ThreadLocalMap instances, so it can only be accessed in this thread. */ public void set(T value) {// Public void set(T value) { ThreadLocalMap map = getMap(t); // If the current thread has a corresponding ThreadLocalMap instance, the current ThreadLocal object is stored as the key and value as the value in the Entry of the ThreadLocalMap. if (map ! = null) map.set(this, value); CreateMap (t, value); createMap(t, value); createMap(t, value); }Copy the code
3, the getMap ()
This method is called when you call the threadlocal.get () method, which returns a reference to threadLocals from the current thread. ThreadLocalMap getMap(Thread t) {return t.htreadlocals; } public class Thread implements Runnable { // ThreadLocal.ThreadLocalMap ThreadLocal.ThreadLocalMap threadLocals = null; }Copy the code
4, the map. The set ()
// Set (key,value); // Set (key,value); Private void set(ThreadLocal<? > key, Object value) {}Copy the code
5, createMap ()
/** * Create a ThreadLocalMap object. * t.htreadlocals is detailed in getMap above. No BB here. * Instantiate ThreadLocalMap and pass in two values, one for the current ThreadLocal object and one for value. */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } // ThreadLocalMap constructor. ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // Focus on !!!!!! // New an internal class Entry of ThreadLocalMap and pass in the key and value. // Key is a ThreadLocal object. table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } /** * ThreadLocal maintains an Entry object within a ThreadLocalMap. The Entry object is key-value, the * key is ThreadLocal, Value is an incoming value *, so any operations we make to a ThreadLocal are actually operations to an internal threadLocalMap.entry *, ensuring that threads don't interfere with each other before they do. * /Copy the code
6, the get ()
/** * gets the value of the entry under the current thread. * Get the ThreadLocalMap of the current thread. Public T get() {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 ! Threadlocalmap. entry e = map.getentry (this); // If entry also gets if (e! + + + + = null) {@suppressWarnings ("unchecked") // Return the value corresponding to the entry directly. T result = (T)e.value; return result; }} // If ThreadLocalMap or Entry is not obtained, set the initial value. // The initial value method is lazy loading, which is used only in get, and is only initialized once. return setInitialValue(); }Copy the code
7, setInitialValue ()
Private T setInitialValue() {// Calls the initialvalue method, provided by the subclass. T value = initialValue(); Thread t = thread.currentThread (); ThreadLocalMap map = getMap(t); If (map! = null) // set map.set(this, value); Else // Not found. Create a map and assign createMap(t, value). // Return the initial value. return value; }Copy the code
8, the initialValue ()
// The implementation is provided by subclasses. // protected protected T initialValue() { return null; }Copy the code
9, the remove ()
/** * removes the value of the current thread-local variable to reduce memory usage. * In fact, when the thread ends, local variables of the thread will be garbage collected automatically, so we do not need to call remove, we call remove only to speed up the memory collection speed. */ public void remove() {// Get the ThreadLocalMap object of the current thread and remove it. ThreadLocalMap m = getMap(Thread.currentThread()); if (m ! = null) m.remove(this); }Copy the code
10 and summary
ThreadLocal becomes so easy by clarifying the following classes.
Thread, ThreadLocal, ThreadLocalMap, Entry
Thread maintains a ThreadLocalMap, and a ThreadLocalMap maintains an Entry that holds a key pair of ThreadLocal and a value of value passed in.
Five, answer questions (interview questions)
1. The difference between Synchronized
Q: It provides the same functionality as Synchronized, which is pretty cool.
A: Bullshit! Synchronization ensures that multiple threads operate on shared variables at the same time and output the correct results. ThreadLocal makes shared variables thread private. Each thread has a separate variable. For an easy-to-understand example of a website counter, you can fix it by adding synchronized to the count++ variable. ThreadLocal can’t do that. It doesn’t post statistics. It can only say how many times each thread has logged in.
2. Which region of the JVM is stored in
Q: Threads are private, so instances and values of ThreadLocal are on the stack?
Answer: No. It’s still stacked. ThreadLocal objects are also objects, which are in the heap. It’s just that the JVM has made its visibility thread visible through some tricks.
Is only the current thread really visible
Q: Is it true that only the current thread is visible?
A: It doesn’t look like it. It looks like we can use the InheritableThreadLocal class to allow multiple threads to access the value of a ThreadLocal, but I haven’t looked into that.
4. Will it cause memory leaks
Q: Will it cause memory leaks?
Answer: Analyze:
Do threadLocalMap. Entry keys leak memory? Does the value of threadLocalmap.entry leak memory? 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; The super(key) class is a weak reference, so there is no memory leak on 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. Strong references and weak references are discarded by GC because the thread terminates and the root node breaks.
Nothing wrong with this analysis, but forgot to one of the main characters, that is the thread pool, the existence of the thread pool thread core is not destroyed, as long as he created repeatedly use, life cycle will not end, but the key is weak references will be GC recycling, value strong reference not recycling, so formed the following condition:
Thread – > ThreadLocalMap – > Entry (the key is null) – > value
Since value and Thread are still connected and reachable, they will not be recycled. In this way, more and more garbage objects are generated but cannot be recycled. In the morning, memory leaks, and after a long time, they must be OOM.
The solution ThreadLocal already has in mind for us, providing the remove() method, which removes the value. So remember remove() when you’re done.
5. Why an Entry array instead of an Entry object
ThreadLocalMap is a reference held in a Thread. Q: Why is the table inside ThreadLocalMap an array instead of a single object?
A: Because your business code can new several ThreadLocal objects, each doing its job. However, in a single request, a Thread has one ThreadLocalMap, not multiple threadLocalMaps. No matter how many times you new a ThreadLocal, there is only one ThreadLocalMap in a Thread, because the reference to a ThreadLocalMap is in the Thread. So it has an Entry array that holds multiple ThreadLocal objects that you create in a thread.
6. Which open source frameworks you learn use ThreadLocal
The Spring framework.
DateTimeContextHolder
RequestContextHolder
7. Are objects in a 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.
8. Write the test
Q: What does this program output? Why is that?
public class TestThreadLocalNpe { private static ThreadLocal threadLocal = new ThreadLocal();
public static void set() { threadLocal.set(1L); } public static long get() { return threadLocal.get(); } public static void main(String[] args) throws InterruptedException { new Thread(() -> { set(); System.out.println(get()); }).start(); Thread.sleep(100); thread.sleep (100); System.out.println(get()); }Copy the code
} a:
1 Exception in thread “main” java.lang.NullPointerException at com.chentongwei.study.thread.TestThreadLocalNpe.get(TestThreadLocalNpe.java:16) at Com. Chentongwei. Study. Thread. TestThreadLocalNpe. Main (TestThreadLocalNpe. Java: 26) why?
Why print a 1 and then a null pointer?
There is nothing wrong with printing 1, and why is the main thread null pointer?
If you answer here
1, 1 Congratulations, you don’t even know what a ThreadLocal is, it’s obviously two threads, child thread and main thread. If the child thread is set to 1, the main thread cannot reach it. ThreadLocal is associated with the thread. It won’t take much to say.
Why is it null?
Since you use long instead of long, you should return null, too. Long is a basic type. The default value is 0. The generic type in ThreadLocal is Long, but the basic type is get, which requires unboxing-null.longvalue (), which is absolutely null.
What looks like a basic Javase topic hides a lot of knowledge.
ThreadLocal utility classes
package com.duoku.base.util; import com.google.common.collect.Maps; import org.springframework.core.NamedThreadLocal; import java.util.Map; /** * Description: * * @author TongWei.Chen 2019-09-09 18:35:30 */ public class ThreadLocalUtil { private static final ThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal("xxx-threadlocal") { @Override protected Map<String, Object> initialValue() { return Maps.newHashMap(); }}; public static Map<String, Object> getThreadLocal(){ return threadLocal.get(); } public static <T> T get(String key) { Map map = threadLocal.get(); // todo:copy a new one return (T)map.get(key); } public static <T> T get(String key,T defaultValue) { Map map = threadLocal.get(); return (T)map.get(key) == null ? defaultValue : (T)map.get(key); } public static void set(String key, Object value) { Map map = threadLocal.get(); map.put(key, value); } public static void set(Map<String, Object> keyValueMap) { Map map = threadLocal.get(); map.putAll(keyValueMap); } public static void remove() { threadLocal.remove(); }}Copy the code
Write in the last