InheritableThreadLocal继承了ThreadLocal,此类扩展了ThreadLocal以提供从父线程到子线程的值的继承:当创建子线程时,子线程接收父线程具有的所有可继承线程局部变量的初始值。 通常子线程的值与父线程的值是一致的。 但是,通过重写此类中的childValue方法,可以将子线程的值作为父线程的任意函数。
public class InheritableThreadLocal extends ThreadLocal {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
*
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
InheritableThreadLocal的源码很短,只有3个很短的方法,我们主要关注getMap()方法和creatMap()方法,这两个方法都是重写的,跟ThreadLocal中的差别在于把ThreadLocal中的threadLocals换成了inheritableThreadLocals,这两个变量都是ThreadLocalMap类型,并且都是Thread类的属性。
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
上文的定义说道了InheritableThreadLocal会继承父线程的值,这是InheritableThreadLocal被创造出来的意义,具体是怎么实现的?
让我们从子线程被创建出来开始看起
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
// ... 省略掉一部分代码
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
// ... 省略掉一部分代码
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];// 创建跟父线程相同大小的table
for (int j = 0; j < len; j++) {// 遍历父线程的inheritableThreadLocals, 在上面第3个代码块作为参数传下来
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal
这是线程被创建的整个流程,从第3个代码块我们可以知道当父线程的inheritableThreadLocals不为空时,当前线程的inheritableThreadLocals属性值会被直接创建,并被赋予跟父线程的inheritableThreadLocals属性一样的值,从最后一个代码块看出来(已在代码中做详细注释)。
此时我们知道了,当一个子线程创建的时候,会把父线程的inheritableThreadLocals属性的值继承到自己的inheritableThreadLocals属性。那么现在的问题是父线程的inheritableThreadLocals属性会有值吗?因为上文提到的ThreadLocal中,我们知道set()方法时,是把键值对放在threadLocals属性。这就要提到刚才说的InheritableThreadLocal重写的getMap()方法,因为InheritableThreadLocal类的set()和get()方法都是用的父类即ThreadLocal的方法,而从ThreadLocal的源码中我们知道,ThreadLocal的get()、set()、remove()方法都会先调用getMap()方法,而InheritableThreadLocal重写了该方法,因此此时返回的ThreadLocalMap为inheritableThreadLocals,所以我们知道了,当定义为InheritableThreadLocal时,操作的属性为inheritableThreadLocals而不是threadLocals。
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
下面代码是对InheritableThreadLocal继承父线程的值的验证,可以看出,子线程确实拿到了父线程的值。
package com.chillax.test;
import java.util.concurrent.TimeUnit;
/**
* InheritableThreadLocal可见性测试
*
* @author JoonWhee
* @author http://blog.csdn.net/v123411739
* @Date 2017年12月2日
*/
public class TestInheritableThreadLocal2 {
public static ThreadLocal threadLocal = new ThreadLocal();
public static ThreadLocal inheritableThreadLocal = new InheritableThreadLocal();
public static ThreadLocal mutableInheritableThreadLocal = new InheritableThreadLocal();
public static ThreadLocal mutableInheritableThreadLocalTo = new InheritableThreadLocal();
public static ThreadLocal immutableInheritableThreadLocal = new InheritableThreadLocal();
public static ThreadLocal immutableInheritableThreadLocalTo = new InheritableThreadLocal();
public static void main(String args[]) throws InterruptedException {
// 测试0.ThreadLocal普通测试;
// 结论0: ThreadLocal下子线程获取不到父线程的值
threadLocal.set(new Integer(123)); // 父线程初始化
Thread thread = new MyThread();
thread.start();
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + threadLocal.get());
System.out.println();
// 测试1.InheritableThreadLocal普通测试;
// 结论1: InheritableThreadLocal下子线程可以获取父线程的值
inheritableThreadLocal.set(new Integer(123)); // 父线程初始化
Thread threads = new MyThreadTo();
threads.start();
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + inheritableThreadLocal.get());
System.out.println();
// 测试2.父线程和子线程的传递关系测试: 可变对象, 父线程初始化;
// 结论2: 父线程初始化, Thread Construct浅拷贝, 共用索引, 子线程先get()对象, 再修改对象的属性,
// 父线程跟着变, 注意: 此处子线程如果没有先get()直接使用set()一个新对象, 父线程是不会跟着变的
mutableInheritableThreadLocal.set(new User("joon"));// 2.1父线程初始化
Thread TestThread = new TestThread(); // 2.2先初始化父线程再创建子线程, 确保子线程能继承到父线程的User
TestThread.start(); // 开始执行子进程
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + mutableInheritableThreadLocal.get()); // 2.5此处输出值为子线程修改的值, 因此可得出上述结论2
System.out.println();
// 测试3.父线程和子线程的传递关系测试: 可变对象, 父线程不初始化;
// 结论3: 父线程没有初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和主线程都是单独引用, 不同对象,
// 子线程修改父线程不跟着变
Thread TestThreadTo = new TestThreadTo(); // 3.1创建子进程
TestThreadTo.start();
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + mutableInheritableThreadLocalTo.get()); // 3.3此处输出为null, 可得出上述结论3
System.out.println();
// 测试4.父线程和子线程的传递关系测试: 不可变对象, 父线程初始化;
// 结论4: 父线程初始化, Thread Construct浅拷贝, 但由于不可变对象由于每次都是新对象, 所以互不影响
immutableInheritableThreadLocal.set("joon");// 4.1父线程初始化
Thread TestThreadTre = new TestThreadTre(); // 4.2先初始化父线程再创建子线程, 确保子线程能继承到父线程的值
TestThreadTre.start(); // 执行子进程
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + immutableInheritableThreadLocal.get()); // 4.5此处输出为父线程的值, 因此可得出上述结论4
System.out.println();
// 测试5.父线程和子线程的传递关系测试: 不可变对象, 父线程不初始化;
// 结论5: 父线程没有初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和父线程操作不同对象, 互不影响
Thread TestThreadFour = new TestThreadFour(); // 5.1创建子线程
TestThreadFour.start();
TimeUnit.MILLISECONDS.sleep(100); // 睡眠, 以等待子线程执行完毕
System.out.println("main = " + immutableInheritableThreadLocalTo.get()); // 5.3此处输出为空, 因此可得出上述结论5
}
private static class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread = " + threadLocal.get());
}
}
private static class MyThreadTo extends Thread {
@Override
public void run() {
System.out.println("inheritableThreadLocal = " + inheritableThreadLocal.get());
}
}
private static class TestThread extends Thread {
@Override
public void run() {
// 2.3此处输出父线程的初始化对象值, 代表子线程确实继承了父线程的对象值
System.out.println("TestThread.before = " + mutableInheritableThreadLocal.get());
// 2.4子类拿到对象并修改
mutableInheritableThreadLocal.get().setName("whee");
System.out.println("mutableInheritableThreadLocal = " + mutableInheritableThreadLocal.get());
}
}
private static class TestThreadTo extends Thread {
@Override
public void run() {
mutableInheritableThreadLocalTo.set(new User("whee"));// 3.2子线程调用set方法
System.out.println("mutableInheritableThreadLocalTo = " + mutableInheritableThreadLocalTo.get());
}
}
private static class TestThreadTre extends Thread {
@Override
public void run() {
// 4.3此处输出父线程初始化的值, 代表子线程确实继承了父线程的对象值
System.out.println("TestThreadTre.before = " + immutableInheritableThreadLocal.get());
// 4.4子线程调用set方法
immutableInheritableThreadLocal.set("whee");
System.out.println("immutableInheritableThreadLocal = " + immutableInheritableThreadLocal.get());
}
}
private static class TestThreadFour extends Thread {
@Override
public void run() {
immutableInheritableThreadLocalTo.set("whee");// 5.2子线程调用set方法
System.out.println("immutableInheritableThreadLocalTo = " + immutableInheritableThreadLocalTo.get());
}
}
private static class User {
String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [name=" + name + "]";
}
}
}
输出结果:
1.对于可变对象:父线程初始化, 因为Thread Construct浅拷贝, 共用索引, 子线程修改父线程跟着变; 父线程不初始化, 子线程初始化, 无Thread Construct浅拷贝, 子线程和父线程都是单独引用, 不同对象, 子线程修改父线程不跟着变。
2.对于不可变对象:不可变对象由于每次都是新对象, 所以无论父线程初始化与否,子线程和父线程都互不影响。
从上面两条结论可知,子线程只能通过修改可变性(Mutable)对象对主线程才是可见的,即才能将修改传递给主线程,但这不是一种好的实践,不建议使用,为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。
虽然说不建议使用,但是有时候还是会碰到这种情况,如果想在修改子线程可变对象,同时不影响主线程,可以通过重写childValue()方法来实现。
package com.chillax.test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 子线程与父线程实现完全互不影响
* @author JoonWhee
* @author http://blog.csdn.net/v123411739
* @Date 2017年12月2日
*/
public class TestInheritableThreadLocal3 {
private static final ThreadLocal
输出结果
TestThread.before = {aa=123}
testThreadLocal = {aa=456}
main = {aa=456}
TestThreadTo.before = {aa=123}
threadLocal = {aa=456}
main = {aa=123}
通过结果,我们可以看出重写childValue()方法确实可以达到使子线程与主线程互不影响的效果。
—————END—————