ThreadLocal

变量值的共享可以使用public static变量的形式实现,所有的线程都使用同一个public static变量,那如何实现每一个线程都有自己的变量呢?JDK提供的ThreadLocal可用于解决这样的问题。

类ThreadLocal的主要作用是将数据放入当前线程对象的Map中,这个Map是Thread类的实例变量。类ThreadLocal自己不管理、不存储任何数据,它只是数据和Map之间的桥梁,用于将数据放入Map中,执行流程如下:
数据->ThreadLocal->currentThread()->Map。

执行后每个线程中的Map存有自己的数据,Map中的key存储的是ThreadLocal对象,value就是存储的值。每个Thread中的Map值只对当前线程可见,其他线程不可以访问当前线程对象中Map的值。当前线程销毁,Map随之销毁,Map中的数据如果没有被引用、没有被使用,则随时GC收回。

由于Map中的key不可以重复,所以一个ThreadLocal对象对应一个value,内存存储结构如下所示:

get()方法与null

如果从未在Thread中的Map存储ThreadLocal对象对应的value值,则get()方法返回null。

/*** @ClassName Run* @Author jia_xx* @Date 2022/6/7 23:43*/
public class Run {public static ThreadLocal t1 = new ThreadLocal();public static void main(String[] args) {if(t1.get() == null){System.out.println("从未放过值");t1.set("我的值");}System.out.println(t1.get());System.out.println(t1.get());}
}
output:
从未放过值
我的值
我的值

类ThreadLocal解决的是变量在不同线程的隔离性,也就是不同线程拥有自己的值,不同线程的值是可以通过ThreadLocal类进行保存的。

类ThreadLocal存取数据流程分析

set()方法

/*** @ClassName Run* @Author jia_xx* @Date 2022/6/7 23:43*/
public class Run {public static void main(String[] args) {ThreadLocal local = new ThreadLocal();local.set("我是任意的值");System.out.println(local.get());}
}
  1. 执行ThreadLocal.set(“我是任意的值”)代码时,ThreadLocal代码如下:
public void set(T value) {Thread t = Thread.currentThread();//对象t就是main线程ThreadLocalMap map = getMap(t);//从main线程中获取ThreadLocalMapif (map != null)map.set(this, value);//不是第一次调用set方法时,map值不是nullelsecreateMap(t, value);//是第一次调用set方法时,执行createMap()方法}
  1. ThreadLocalMap map = getMap(t);源码如下:
ThreadLocalMap getMap(Thread t) {//参数t就是前面传入的main线程return t.threadLocals;//返回main线程中threadLocals变量对应的ThreadLocalMap对象}//此源代码在ThreadLocal.java类中
  1. 声明变量t.threadLocals的源代码如下:
public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;//默认值为null
}//此源代码在Thread.java类中

对象threadLocals数据类型就是ThreadLocal.ThreadLocalMap,变量threadLocals是Thread类中的实例变量。

  1. 取得Thread中的ThreadLocal.ThreadLocalMap对象后,第一次向其存放数据时会调用createMap()方法来创建ThreadLocal.ThreadLocalMap对象,并向这个新的ThreadLocalMap对象存储数据,ThreadLocalMap中的key就是当前的ThreadLocal对象,值就是传入的value,createMap()方法源码如下:
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}//此源代码在ThreadLocal.java类中

实例化ThreadLocalMap的时候,向构造方法传入this和firstValue,参数this就是当前ThreadLocal对象,firstValue就是调用ThreadLocal对象set()方法传入的参数值。

  1. new ThreadLocalMap(this, firstValue);源码如下:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);
}

源码中发现,ThreadLocal对象与firstValue被封装进Entry对象中,并放入table[]数组中。

  1. table[]数组的源代码如下:
static class ThreadLocalMap {private Entry[] table;
}

变量table就是Entry[]数组类型。

经过上面一系列步骤,成功将value通过ThreadLocal放入当前线程currentThread()中的ThreadLocal.ThreadLocalMap对象中。

get()方法

当代码执行到System.out.println(local.get());此时local.get()源码如下:

public T get() {Thread t = Thread.currentThread();//t就是main线程ThreadLocalMap map = getMap(t);//从main线程中获得mapif (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);//以this作为key,获得对应的Entry对象if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;//从Entry对象中取得value并返回return result;}}return setInitialValue();
}private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);
}

以上就是get()方法执行过程

为什么不直接向Thread类中的ThreadLocalMap对象存取数据呢?

为什么不直接向Thread类中的ThreadLocalMap对象存取数据呢?原因如下:

ThreadLocal.ThreadLocalMap threadLocals = null;//默认值为null

变量threadLocals默认是包访问级别,所以不能直接从外部访问该变量,只有同包中的类可以访问threadLocals变量,而ThreadLocal和Thread恰好在同一个包(java.lang)中,所以外部代码通过ThreadLocal可以访问Thread类中的“秘密对象”-ThreadLocalMap。

验证线程变量的隔离性

以下示例来验证通过ThreadLocal可向每个线程中存储自己的私有数据。

public class Tools {public static ThreadLocal t1 = new ThreadLocal();
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {Tools.t1.set("A" + (i + 1));System.out.println("A get " + Tools.t1.get());int sleepValue = (int) (Math.random() * 1000);Thread.sleep(sleepValue);}} catch (Exception e) {e.printStackTrace();}}
}
public class MyThreadB extends Thread{@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {Tools.t1.set("B " + (i + 1));System.out.println("B get " + Tools.t1.get());int sleepValue = (int) (Math.random() * 1000);Thread.sleep(sleepValue);}} catch (Exception e) {e.printStackTrace();}}
}
public class Test {public static void main(String[] args) throws InterruptedException {MyThreadA a = new MyThreadA();MyThreadB b = new MyThreadB();a.start();b.start();for(int i=0;i<10;i++){Tools.t1.set("main "+(i+1));System.out.println("    main get "+Tools.t1.get());int sleepValue = (int)(Math.random()*1000);Thread.sleep(sleepValue);}}
}
output:main get main 1
A get A 1
B get B 1main get main 2main get main 3main get main 4
A get A 2
B get B 2
B get B 3
B get B 4
A get A 3
A get A 4main get main 5
A get A 5
A get A 6
B get B 5main get main 6
A get A 7
B get B 6main get main 7
A get A 8
A get A 9
A get A 10
B get B 7
B get B 8main get main 8
B get B 9main get main 9
B get B 10main get main 10

程序运行结果表明通过ThreadLocal可向每一个线程存储自己的私有数据,虽然3个线程都向t1对象中set()数据值,但每个线程还是能取出自己的数据,不能取出别的线程的。

解决get()方法返回null的问题

在第一次调用ThreadLocal类的get()方法时,返回值是null,怎样实现第一次调用get()不返回null呢?也就是说,怎样使其具有默认值的效果呢?

public class ThreadLocalExt extends ThreadLocal {@Overrideprotected Object initialValue() {return "我是默认值 第一次get不再为null";}
}
protected T initialValue() {return null;
}

覆盖initialValue()方法具有初始值,ThreadLocal中的initialValue()方法默认返回值是null,所以在子类中重写。

public class Run {public static ThreadLocalExt t1 = new ThreadLocalExt();public static void main(String[] args) {if (t1.get() == null) {System.out.println("从未放过值");t1.set("我的值");}System.out.println(t1.get());System.out.println(t1.get());}
}

验证重写initialValue()方法的隔离性

public class Tools {public static ThreadLocalExt t1 = new ThreadLocalExt();
}
public class ThreadLocalExt extends ThreadLocal {@Overrideprotected Object initialValue() {return System.currentTimeMillis();}
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("在ThreadA线程中取值="+Tools.t1.get());}} catch (Exception e) {e.printStackTrace();}}
}
public class Test {public static void main(String[] args) throws InterruptedException {for(int i=0;i<10;i++){System.out.println("    在Main线程中取值="+Tools.t1.get());}Thread.sleep(5000);MyThreadA a = new MyThreadA();a.start();}
}
output:在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060在Main线程中取值=1654700997060
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074
在ThreadA线程中取值=1654701002074

程序表明子线程和父线程各有自己所拥有的值。

InheritableThreadLocal

使用类InheritableThreadLocal可使子线程继承父线程的值。

类ThreadLocal不能实现值继承

public class Tools {public static ThreadLocal t1 = new ThreadLocal();
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("在ThreadA线程中取值="+Tools.t1.get());Thread.sleep(100);}} catch (Exception e) {e.printStackTrace();}}
}
public class Run {public static void main(String[] args) throws InterruptedException {for(int i = 0;i<10;i++){if(Tools.t1.get() == null){Tools.t1.set("此值是main线程放入的");}System.out.println("    在Main线程中取值="+Tools.t1.get());}Thread.sleep(5000);MyThreadA threadA = new MyThreadA();threadA.start();}
}
output:在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null
在ThreadA线程中取值=null

使用InheritableThreadLocal体现值继承特性

使用InheritableThreadLocal可以让子线程从父线程继承值。

将以上案例的Tools类替换为如下:

public class Tools {public static InheritableThreadLocal t1 = new InheritableThreadLocal();
}
output:在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的在Main线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的

子线程MyThread线程获取的值是从父线程main继承的。

值继承性在源代码中的执行流程

  1. 分析类InheritableThreadLocal源码,如下:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {protected T childValue(T parentValue) {return parentValue;}ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}
}

InheritableThreadLocal类的源代码中存在3个方法,这3个方法都是对父类ThreadLocal中同名方法进行重写,但在源码中并没有使用@Override进行标识,所以初期分析比较复杂。

  1. 在main()方法中使用main线程执行InheritableThreadLocal.set()方法,源码如下:
public class Run {public static void main(String[] args) throws InterruptedException {for(int i = 0;i<10;i++){if(Tools.t1.get() == null){Tools.t1.set("此值是main线程放入的");//此处执行}System.out.println("    在Main线程中取值="+Tools.t1.get());}Thread.sleep(5000);MyThreadA threadA = new MyThreadA();threadA.start();}
}

调用InheritableThreadLocal对象的set()方法其实就是调用ThreadLocal.java类中的set方法,因为InheritableThreadLocal并没有重写set()方法。

public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//重写if (map != null)map.set(this, value);elsecreateMap(t, value);//重写
}

执行ThreadLocal类中的set()方法时,有两个方法已经被InheritableThreadLocal类重写了,分别是getMap(t)和createMap(t,value),所以实际调用的是InheritableThreadLocal类中的这两个方法,重写源码如下:

ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;
}void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
  1. 通过查看重写方法可以明确,不再向Thread类中的ThreadLocal.ThreadLocalMap threadLocals存入数据,而是向ThreadLocal.ThreadLocalMap inheritableThreadLocals存入数据,这两个对象在Thread.java中声明如下:
public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

上述分析说明main线程向inheritableThreadLocals对象存入数据,对象inheritableThreadLocals就是存储数据的容器,子线程如何实现从父线程中的inheritableThreadLocals对象继承值呢?

  1. 这个实现思路就是在创建子线程MyThreadA时,子线程主动引用父线程main中的inheritableThreadLocals对象值,源码如下:
public class Thread implements Runnable {private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {......if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);}
}

方法init(ThreadGroup g, Runnable target, String name,long stackSize,AccessControlContext acc, boolean inheritThreadLocals)是被Thread的构造方法调用的,所以在new ThreadA()时,Thread.java源码内部会自动调用该init方法,在该init方法中最后一个参数boolean inheritThreadLocals代表当前线程对象是否会从父线程继承值,每一次都会继承值,因为这个值被永远传入true,传入true的源码在如下方法中:

private void init(ThreadGroup g, Runnable target, String name,long stackSize) {init(g, target, name, stackSize, null, true);
}
  1. if (inheritThreadLocals && parent.inheritableThreadLocals != null)左边永远为true,那么计算右边parent.inheritableThreadLocals != null,当main线程中的inheritableThreadLocals存放数据时,对象inheritableThreadLocals并不为null,表达式为true,所以程序继续执行,对当前线程的inheritableThreadLocals赋值,源码如下:
this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  1. 代码中的this就是MyThreadA线程对象,createInheritedMap源码如下:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];//新建Entry数组for (int j = 0; j < len; j++) {Entry e = parentTable[j];if (e != null) {@SuppressWarnings("unchecked")ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();if (key != null) {Object value = key.childValue(e.value);Entry c = new Entry(key, value);//实例化新的Entry对象int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)h = nextIndex(h, len);table[h] = c;//将父线程中的数据复制到新数组中size++;}}}
}
  1. 分析new ThreadLocalMap核心代码,子线程将父线程中的table对象以复制的方式赋值给子线程的table数组,这个过程在Thread对象创建时发生,也就是说子线程创建完毕后,子线程的数据就是主线程中的旧的数据,主线程使用新的数据时,子线程还是使用旧的数据,因为主子线程使用两个Entry[]对象数组各自存储自己的值。

父线程有最新的值,子线程仍旧是旧值

public class Tools {public static InheritableThreadLocal t1 = new InheritableThreadLocal();
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("在ThreadA线程中取值="+Tools.t1.get());Thread.sleep(1000);}} catch (Exception e) {e.printStackTrace();}}
}
public class Run {public static void main(String[] args) throws InterruptedException {if(Tools.t1.get() == null){Tools.t1.set("此值是main线程放入的");}System.out.println("    在Main线程中取值="+Tools.t1.get());Thread.sleep(100);MyThreadA threadA = new MyThreadA();threadA.start();Thread.sleep(5000);Tools.t1.set("此值是main线程new放入的");}
}
output:在Main线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的

子线程有最新的值,父线程仍旧是旧值

public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("在ThreadA线程中取值="+Tools.t1.get());Thread.sleep(1000);if(i == 5){Tools.t1.set("我是ThreadA新设置的最新的值========");}}} catch (Exception e) {e.printStackTrace();}}
}
public class Run {public static void main(String[] args) throws InterruptedException {if(Tools.t1.get() == null){Tools.t1.set("此值是main线程放入的");}System.out.println("    在Main线程中取值="+Tools.t1.get());Thread.sleep(100);MyThreadA threadA = new MyThreadA();threadA.start();Thread.sleep(3000);for(int i = 0;i<10;i++){System.out.println("main end get value="+Tools.t1.get());Thread.sleep(1000);}}
}
output:
在Main线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
main end get value=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
main end get value=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
main end get value=此值是main线程放入的
在ThreadA线程中取值=此值是main线程放入的
main end get value=此值是main线程放入的
在ThreadA线程中取值=我是ThreadA新设置的最新的值========
main end get value=此值是main线程放入的
在ThreadA线程中取值=我是ThreadA新设置的最新的值========
main end get value=此值是main线程放入的
在ThreadA线程中取值=我是ThreadA新设置的最新的值========
main end get value=此值是main线程放入的
在ThreadA线程中取值=我是ThreadA新设置的最新的值========
main end get value=此值是main线程放入的
main end get value=此值是main线程放入的
main end get value=此值是main线程放入的

子线程可以感应对象属性值的变化

上述都是在主、子线程中使用String数据类型做继承特性的演示,当子线程从父线程继承可变对象数据类型时,子线程可以取到最新对象中的属性值。

public class UserInfo {private String username;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}public class Tools {public static InheritableThreadLocal<UserInfo> t1 = new InheritableThreadLocal();
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {UserInfo userInfo = Tools.t1.get();System.out.println("在ThreadA线程中取值="+userInfo.getUsername()+" "+userInfo.hashCode());Thread.sleep(1000);}} catch (Exception e) {e.printStackTrace();}}
}
public class Run {public static void main(String[] args) throws InterruptedException {UserInfo userInfo = new UserInfo();System.out.println("A userInfo "+userInfo.hashCode());userInfo.setUsername("中国");if(Tools.t1.get() == null){Tools.t1.set(userInfo);}System.out.println("在Main线程中取值="+userInfo.getUsername()+" "+userInfo.hashCode());Thread.sleep(100);MyThreadA threadA = new MyThreadA();threadA.start();Thread.sleep(5000);Tools.t1.get().setUsername("巴基斯坦");}
}
output:
A userInfo 1229416514
在Main线程中取值=中国 1229416514
在ThreadA线程中取值=中国 1229416514
在ThreadA线程中取值=中国 1229416514
在ThreadA线程中取值=中国 1229416514
在ThreadA线程中取值=中国 1229416514
在ThreadA线程中取值=中国 1229416514
在ThreadA线程中取值=巴基斯坦 1229416514
在ThreadA线程中取值=巴基斯坦 1229416514
在ThreadA线程中取值=巴基斯坦 1229416514
在ThreadA线程中取值=巴基斯坦 1229416514
在ThreadA线程中取值=巴基斯坦 1229416514

重写childValue()方法实现对继承的值进行加工

public class Tools {public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
public class InheritableThreadLocalExt extends InheritableThreadLocal {@Overrideprotected Object initialValue(){return System.currentTimeMillis();}@Overrideprotected Object childValue(Object parent){return parent+" 我在子线程加的~!";}
}
public class MyThreadA extends Thread {@Overridepublic void run() {try {for (int i = 0; i < 10; i++) {System.out.println("在ThreadA中取值="+Tools.t1.get());Thread.sleep(1000);}} catch (Exception e) {e.printStackTrace();}}
}
public class Run {public static void main(String[] args) throws InterruptedException {for(int i = 0;i<10;i++){System.out.println("在Main线程中取值="+ Tools.t1.get());}Thread.sleep(500);MyThreadA threadA = new MyThreadA();threadA.start();}
}
output:
在Main线程中取值=1654790289926
在Main线程中取值=1654790289926
在Main线程中取值=1654790289926
在Main线程中取值=1654790289926
在Main线程中取值=1654790289926
在Main线程中取值=1654790289926
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!
在ThreadA中取值=1654790289926 我在子线程加的~!

通过重写childValue()方法,子线程可以对父线程继承的值进行加工修改。

子线程可以在任意时间执行set()方法具有最新的值,而重写childValue()方法实现子线程具有最新的值只有在创建子线程时才会发生,仅仅一次。

ThreadLocal与InheritableThreadLocal源码解析相关推荐

  1. 面试官系统精讲Java源码及大厂真题 - 43 ThreadLocal 源码解析

    43 ThreadLocal 源码解析 引导语 ThreadLocal 提供了一种方式,让在多线程环境下,每个线程都可以拥有自己独特的数据,并且可以在整个线程执行过程中,从上而下的传递. 1 用法演示 ...

  2. HandlerThread和IntentService源码解析

    简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将这两者放在一起分析. HandlerThread: HandlerThread 其实是Handler ...

  3. EventBus源码解析

    前面一篇文章讲解了EventBus的使用,但是作为开发人员,不能只停留在仅仅会用的层面上,我们还需要弄清楚它的内部实现原理.所以本篇博文将分析EventBus的源码,看看究竟它是如何实现"发 ...

  4. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

  5. spring 注解试事物源码解析

    spring 注解试事物源码解析 基于xml注解式事务入口 public class TxNamespaceHandler extends NamespaceHandlerSupport {stati ...

  6. Android源码解析(一)动画篇-- Animator属性动画系统

    Android源码解析-动画篇 Android源码解析(一)动画篇-- Animator属性动画系统 Android源码解析(二)动画篇-- ObjectAnimator Android在3.0版本中 ...

  7. Handler消息机制(九):IntentService源码解析

    作者:jtsky 链接:https://www.jianshu.com/p/0a150ec09a32 简介 首先我们先来了解HandlerThread和IntentService是什么,以及为什么要将 ...

  8. EventBus3.0源码解析

     本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和 ...

  9. [Android] Handler源码解析 (Java层)

    之前写过一篇文章,概述了Android应用程序消息处理机制.本文在此文基础上,在源码级别上展开进行概述 简单用例 Handler的使用方法如下所示: Handler myHandler = new H ...

最新文章

  1. 陈程杰、夏瑞:数据分析工具TBtools介绍和操作视频+公众号/社群
  2. 基于uPC1677C射频功率放大
  3. Google发布了能理解人类语言的云服务
  4. some language grammars
  5. jenkins+ant+git+android搭建笔记
  6. poj1273 最大流
  7. matlab练习程序(各向异性扩散)
  8. 第二章 数据的表示和运算 2.1.4 奇偶校验 [计算机组成原理笔记]
  9. 排序算法java 简书_常用的排序算法(Java版)
  10. Android tftp服务器,Ubuntu下配置TFTP服务以及 android下使用TFTP
  11. vue 针试打印机实现
  12. MAVEN 仓库加速 阿里云镜像
  13. FastFDS 分布式文件系统
  14. win 10连上wifi 无Internet 解决方法
  15. 05 无人机动态模型
  16. web连接mysql教程视频_jsp servlet mysql实现的Java web在线商城项目源码附带视频指导运行教程...
  17. 什么是射频中经常提到的IQ信号?
  18. cve-2019-0708漏洞复现
  19. 欧克:优秀网站是如何炼成的
  20. opencv2矩阵操作

热门文章

  1. Android P 源码分析 4 - logd 的初始化
  2. 雅的“小生意思维”让唯品会在夹缝中滋润生长
  3. 夜游经济:夜景“亮化”,形象“美化”,经济“活化”
  4. 北大的计算机应用基础,2012北大计算机应用基础 统考资料
  5. 惠普星14黑苹果之路
  6. CPU高速缓存那些事儿
  7. 详细版【全连接前馈神经网络】(邱锡鹏)
  8. 安拆网:振动打桩 液压振动锤打拔拉森钢板桩施工浅析
  9. 6面阿里拿下30K*15薪Offer!聊聊面试要注意的二三事...
  10. 通过银行卡号得到所属银行,卡类型及获取银行图片