一,android移动应用开发答案第二版

*/
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;
}
}

}

结构大概如下: 一,android移动应用开发答案第二版

它其实和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方法。

上一篇:ConnectionUtils类代码示例


下一篇:让你彻底理解ThreadLocal原理和应用