*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
- 上面的代码比较容易理解,上面三个方法都用到了getMap方法
/**
-
Get the map associated with a ThreadLocal. Overridden in
-
InheritableThreadLocal.
-
@param t the current thread
-
@return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//ThreadLocalMap为Thread的成员变量。每个Thread都有自己的ThreadLocalMap。到此同学们应该知道为什么ThreadLocal为什么可以实现线程间数据隔离了,就是因为数据都存储在ThreadLocalMap中。
} -
ThreadLocalMap
从名字看它就是一个map,和大家熟悉的HashMap有些类似,但它有自己特点,咱们慢慢来分析它。
//ThreadLocal静态内部类
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 expunged 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;
}
}
…
}
结构大概如下:
它其实和HashMap很像,但是它没有链表和红黑树接口,那么遇到hash冲突怎么解决呢?
/**
- Set the value associated with key.
- @param key the thread local object
- @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don’t use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {//当前数据位置已存在,并且key相同直接替换value
e.value = value;
return;
}
if (k == null) {//当前数据位置为null,创建一个entry放进去
replaceStaleEntry(key, value, i);
return;
}
//这个循环还有一个隐藏的条件,如果当前位置数据已经存在,但是key不相等,则执行下一次循环 “e = tab[i = nextIndex(i, len)]”,继续查找下一个位置存放。
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//这个循环还有一个隐藏的条件,如果当前位置数据已经存在,但是key不相等,则执行下一次循环 “e = tab[i = nextIndex(i, len)]”,继续查找下一个位置存放。
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6dc994f8
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
d4f448fca89a394166212654~tplv-k3u1fbpfcp-watermark.image)
- 共享数据 inheritableThreadLocals
//使用方式
private void inheritableThreadLocalTest() {
inheritableThreadLocal.set(“mainThread value”);//在mainThread存入值
new Thread(“Thread~1”) {
@Override
public void run() {
super.run();
//Thread~1 的parent是mainThread
String value = inheritableThreadLocal.get();
System.out.println("value = " + value + " ,currentThreadName: " + Thread.currentThread().getName());//value = mainThread value ,currentThreadName: Thread~1
}
}.start();
}
//源码
public
class Thread implements Runnable {
/* 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.
*/
// 大家注意到Thread类中,除了threadLocals,还有一个成员变量inheritableThreadLocals,根据名字可猜测是可继承的threadlocals
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
/**
- Initializes a Thread.
- @param g the Thread group
- @param target the object whose run() method gets called
- @param name the name of the new Thread
- @param stackSize the desired stack size for the new thread, or
-
zero to indicate that this parameter is to be ignored.
- @param acc the AccessControlContext to inherit, or
-
AccessController.getContext() if null
- @param inheritThreadLocals if {@code true}, inherit initial values for
-
inheritable thread-locals from the constructing thread
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
…省略部分代码
//1.这里复制(继承)了parent的inheritableThreadLocals,好奇parent是什么的小伙伴,看一下源码init中,Thread parent = currentThread();它是创建该线程的线程。
//2.如何给parent的inheritableThreadLocals赋值呢?它的权限是默认的,我们无法在我们的代码中引入,需要借助jdk封装好的InheritableThreadLocal,这个源码很简单就不带大家看了。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
- ThreadLocal中的内存泄漏问题
static class ThreadLocalMap {
//Entry是弱引用对象,ThreadLocal是被弱应用对象,它会被gc回收,同学们别搞混了
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
…
}
ThreadMap只有key是弱引用,value依然是强引用。
引用链如下:
Thread----ThreadLocalMap----Entry----value
当threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收,但是value依然不能回收(有一条强引用链),直到Thread销毁,value才能被回收。但是线程池中,线程不会销毁的情况下这种问题就无法解决了。
解决方案:调用remove方法。
dLocalMap----Entry----value
当threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收,但是value依然不能回收(有一条强引用链),直到Thread销毁,value才能被回收。但是线程池中,线程不会销毁的情况下这种问题就无法解决了。
解决方案:调用remove方法。