分析ThreadLocal的先从初始化开始分析
上篇中初始化的代码如下 ThreadLocal的介绍以及示例
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());
private static final InheritableThreadLocal<Integer> threadId = new InheritableThreadLocal<Integer>() {
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
两个方法都是重载了方法initialValue,只是第一种方法更加巧妙一些,其实本质都一样,详细区别可以见上篇文章
那在初始化对象执行线程中主要是通过
MyThreadLocal.get();
来获取对应的初始化值,那整个源码的分析过程就从这个get方法开始
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
首先获取ThreadLocal中维护的map,如果这个map中已经有了这个值,则直接返回这个值,如果没有则通过setInitialValue来设置值,这里需要注意的是这个ThreadLocalMap
下面看看ThreadLocalMap的部分代码
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expuniged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
从描述来理解,就是这个Entry是一个弱引用类型,如果ThreadLocal的对象被置为空之后,entry.get()也会返回空,这时候就意味着这个key不在使用,所以这个entry可以从table中清除掉。这些entries会被称作 过期的entries 在随后的代码中。
从ThreadLocalMap的整个源码来还是很容易看懂的,这个map是用的Entry数组去存放数据,在调用getEntry方法的时候首先通过key的hash值取找到对应Entry数组的下标,通过下标来获取对应的Entry,如果调用e.get()返回空则以为着对象被回收掉了,这时候需要把对应被回收掉的Reference从Entry数组中清除掉,让系统可以回收内存
从上面的getMap方法可以看到,是通过这个线程持有的threadLocals去寻找是否有这个ThreadLocal对应value,如果有则返回,没有则初始化,初始化代码如下
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
看起来还是比较简单的,调用重载的initialValue()获取到对应的value,然后set到对应的map对象中。
上面就是ThreadLocal的整个调用过程了
下面需要讲讲InheritableThreadLocal是如何在父子类线程中传播对象的
首先看Thread的源码,看他的init的方法中(这个方法在线程被初始化的过程中被调用)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
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();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
主要看到最后一段
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
如果这个线程的父类线程的inheritableThreadLocals 不为空,则调用
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)
这里就是把父类线程的值设置到子类线程中去的一个方法,那么看看InheritableThreadLocal如何保证inheritableThreadLocals不为空并且怎么实现createInheritedMap这个方法来实现值的复制的呢?
看看InheritableThreadLocal的源码
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.
*
* 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中的部分源码
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
这里调用createInheritedMap返回了ThreadLocalMap对象
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
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);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
然后这个方法中的
Object value = key.childValue(e.value);
就是调用了InheritableThreadLocal的childValue方法直接把传入的值返回出去,并且从getMap方法就可以看到当使用InheritableThreadLocal的时候获取的map就从之前的threadLocals变成了inheritableThreadLocals,通过定义好的inheritableThreadLocals来实现了父子类线程对象的传递。
以上就是所有源码解析的部分了,如有不理解的地方可以在下方留言哦~~~~