Java的Therdlocal_Java:ThreadLocal

结构变化后带来的好处

早期ThreadLocalMap里的Entry较多,每个线程都有一个,占用空间大

线程销毁时,后期的ThreadLocalMap将会销毁,释放资源

设置默认值,也就是当前线程和ThreadLocal对象还未往ThreadLocalMap里存数据时

ThreadLocal threadLocal2 = new ThreadLocal(){

@Override

protected String initialValue() {

return "hello";

}

};

System.out.println(threadLocal2.get());

因重写了ThreadLocal里的方法,该对象实际上是生成的一个匿名类,并继承了ThreadLocal

JhxRAAAAAxJREFUeNpiYAAIMAAAAgABT21Z4QAAAABJRU5ErkJggg==

我们看看ThreadLocal如何存数据的

获取当前线程的ThreadLocalMap

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

ThreadLocalMap的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];//遍历当前线程的ThreadLocalMap

e != null;

e = tab[i = nextIndex(i, len)]) {

ThreadLocal> k = e.get();

if (k == key) {//发现key,则覆盖value

e.value = value;

return;

}

if (k == null) {//发现某个key对应的ThreadLocal对象已被回收

replaceStaleEntry(key, value, i);//详见下方解析

return;

}

}

tab[i] = new Entry(key, value);//没有找到key,说明是第一次存这个key

int sz = ++size;

if (!cleanSomeSlots(i, sz) && sz >= threshold)

rehash();

new Entry

static class Entry extends WeakReference> {

/** The value associated with this ThreadLocal. */

Object value;

Entry(ThreadLocal> k, Object v) {

super(k);//调用WeakReference的构造方法,使ThreadLocalMap里的key与ThreadLocal对象保持弱引用的关系,即不影响该对象的gc

value = v;

}

}

上面的代码中,如果发现ThreadLocalMap里有key为null,会执行replaceStaleEntry(key, value, i);该方法里调用了expungeStaleEntry(int staleSlot)

private int expungeStaleEntry(int staleSlot) {

Entry[] tab = table;

int len = tab.length;

// expunge entry at staleSlot

tab[staleSlot].value = null;//清理该key对应的value

tab[staleSlot] = null;//清理该key

size--;

// Rehash until we encounter null

Entry e;

int i;

for (i = nextIndex(staleSlot, len);//遍历ThreadLocalMap

(e = tab[i]) != null;

i = nextIndex(i, len)) {

ThreadLocal> k = e.get();

if (k == null) {//清理key为null的Entry

e.value = null;

tab[i] = null;

size--;

} else {

int h = k.threadLocalHashCode & (len - 1);//重新计算hash

if (h != i) {//如果不是当前位置,将移动

tab[i] = null;//置空当前位置的Entry

// Unlike Knuth 6.4 Algorithm R, we must scan until

// null because multiple entries could have been stale.

while (tab[h] != null)//如果重新计算哈希得出的位置有值了,就往后找,直到找到个不为空的

h = nextIndex(h, len);

tab[h] = e;

}

}

}

return i;

}

上诉方法是可以防止内存泄露,将为null的key对应的Entry清理。

ThreadLocalMap使用的是线性探查法,与此相关的算法有HashMap的拉链法

JhxRAAAAAxJREFUeNpiYAAIMAAAAgABT21Z4QAAAABJRU5ErkJggg==

回收过程

ThreadLocal引用销毁(这个词可能不专业)

ThreadLocal对象因没有强引用,只有个弱引用,gc时将回收。这就是为什么用弱引用,使用强引用时,会影响ThreadLocal对象的回收

如果ThreadLocal对象被回收了,ThreadLocalMap里依旧存着该对象的虚引用为key的Entry(包括Value),这将会造成内存泄漏,如何避免呢?

实际代码中,ThreadLocalMap会在set,get以及resize等方法中对stale slots做自动删除(set以及get不保证所有过期slots会在操作中会被删除,而resize则会删除threadLocalMap中所有的过期slots)。当然将threadLocal对象设置为null并不能完全避免内存泄露对象,最安全的办法仍然是调用ThreadLocal的remove方法,来彻底避免可能的内存泄露。

ThreadLocal对象可设置为静态变量,这样在任何地方都能方便的获取到该对象,执行remove。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值