ThreadLocal在是多线程操作中一个常用的工具类,主要为每个线程提供一个本地副本,避免共享变量的产生。但是他有一个尴尬的情况,就是不支持父子线程之间的传递,但在实际工作中还是会有这样的需求,所以就出现了InheritableThreadLocal,因为之前了解过ThreadLocal的实现,所以对InheritableThreadLocal产生了好奇。
打开InheritableThreadLocal,继承了ThreadLocal,重写了几个方法,主要就是将Thread中的threadLocals替换成了inheritableThreadLocals,在贴张Thread源码的截图应该就清晰了
java
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* 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.
* <p>
* 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);
}
}
其余的就跟ThreadLocal流程相同了,这样就很奇怪了,为什么重写了这几个方法就可以进行父子线程的传递,那答案就只能在Thread的实现上,不论是ThreadLocal或者InheritableThreadLocal在我的理解里,他们其实更像工具类,真正的实现线程上的交互还是在Thread进行的。
java
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
java
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
// 获取当前线程,也就是当前Thread启动后的父线程
Thread parent = currentThread();
// java线程安全相关
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
//是否是守护线程
this.daemon = parent.isDaemon();
//设置优先级
this.priority = parent.getPriority();
//线程名称
this.name = name.toCharArray();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
// 赋值runnable
this.target = target;
setPriority(priority);
//判断父线程是否构建了InheritableThreadLocals
if (parent.inheritableThreadLocals != null)
// 在当前ThreadL对象中构建InheritableThreadLocals对象,将父线程的InheritableThreadLocals对象赋值给当前ThreadL对象
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
Thread在初始化的时候,会判断当前线程是否有inheritableThreadLocals,有的话就会在当前Thread类构建一个相同的inheritableThreadLocals对象,这里有点点绕的就是currentThread()却被称为父线程,因为构建Thread的时候,新的线程还没开启,所以currentThread()获取的线程还是原来的线程。点开createInheritedMap会发现他调用的是ThreadLocalMap方法,会发现有两个构造方法,但是目的是完全不同的。
java
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
//构建新的ThreadLocalMap
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始16
table = new Entry[INITIAL_CAPACITY];
//hash
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
java
// 根据父线程的ThreadLocalMap构建新的ThreadLocalMap,新的ThreadLocalMap包含父类所有的ThreadLocalMap
private ThreadLocalMap(ThreadLocalMap parentMap) {
//获取父类所有的map
Entry[] parentTable = parentMap.table;
// 获取父类map的长度
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
// 获取父类map的元素
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);
int h = key.threadLocalHashCode & (len - 1);
// 防止hash冲突,找到空的位置插入
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
总的来说InheritableThreadLocal和ThreadLocal的增删改查的逻辑基本相同的,主要还是Thread中多了一个传递的逻辑。