ThreadLocal allows us to create thread-private variables. This variable is invisible to other threads. ThreadLocal creates a copy of the variable in each thread. Each thread can access its own private thread variables. The code example is as follows:
public class ThreadLocalDemo { //创建一个ThreadLocal对象,用来为每个线程会复制保存一份变量,实现线程封闭 private static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 1; } }; public static void main(String[] args) { //线程0 new Thread(){ @Override public void run() { localNum.set(1); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } localNum.set(localNum.get()+10); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//11 } }.start(); //线程1 new Thread(){ @Override public void run() { localNum.set(3); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } localNum.set(localNum.get()+20); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//23 } }.start(); System.out.println(Thread.currentThread().getName()+":"+localNum.get());//0 } }
As mentioned above, counting the main thread and the two newly created threads, there are a total of three threads. Each thread contains its own private thread variables. Variable, here we set the value 1. The set() and get() methods are used to set and obtain the value. The execution results are as follows:
ThreadLocal is a generic class that can accept any type of object. It maintains a static internal class of ThreadLocalMap. The get(), set(), etc. we use actually come from this class, and will be The current thread creates a ThreadLocalMap object to record private values
First look at the set() method
public void set(T value) { //拿到当前线程 Thread t = Thread.currentThread(); //拿到当前线程map ThreadLocalMap map = getMap(t); if (map != null) //存在设置值 map.set(this, value); else //不存在则创建 createMap(t, value); }
void createMap(Thread t, T firstValue) { //threadLocals属性即为此map t.threadLocals = new ThreadLocalMap(this, firstValue); }
Then the get() method
public T get() { //拿到当前线程 Thread t = Thread.currentThread(); //拿到当前线程对应的map ThreadLocalMap map = getMap(t); //如果已有map if (map != null) { //取值操作, 拿到对应的Entry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //没有map, 则去创建初始化一个map return setInitialValue(); }
private T setInitialValue() { //initialValue()方法返回的value为null T value = initialValue(); //拿到当前线程去创建对应的map Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
ThreadLocal can be understood as an encapsulation of ThreadLocalMap
In ThreadLocalMap, the ThreadLocal object uses weak references as keys
In this case, if a ThreadLocal does not have an external strong reference, then the key is destined to be recycled by GC, which will cause the key in the ThreadLocalMap to be null, and the value still has a strong reference chain
A thread can With multiple ThreadLocals at the same time, if the key as a weak reference is recycled, the value cannot be recycled, then this will cause the life cycle of this ThreadLocal to be as long as this thread (because the strong reference chain of this value after the thread is executed will be interrupted), if the thread never ends and the accumulated value cannot be recycled, then a memory leak problem will occur.
The way to solve the problem here is: call ThreadLocal every time after using it The remove() method clears data
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
Here we look at the difference between key as a strong reference
If the key is used as a strong reference, then its life cycle is as long as the thread, and there is a stable Strong reference chains cannot be recycled, causing memory leaks. If they are used as weak references, the GC will automatically recycle them, and the value can be better recycled in the subsequent remove() method, so we generally use ThreadLocal Designed to be private static, use the remove() method to manually delete them after use
The above is the detailed content of What is the implementation principle of ThreadLocal thread variables in Java. For more information, please follow other related articles on the PHP Chinese website!