ThreadLocal用途
ThreadLocal主要用于多线程之间隔离变量和线程内共享数据。
ThreadLocal保证线程安全,因为其特殊的结构,不存在多线程之间共享数据,也不会存在线程同步锁带来的性能消耗。
ThreadLocal用法
private static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
private static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
integerThreadLocal.set(finalI);
stringThreadLocal.set(finalI % 2 == 0 ? "偶数" : "奇数");
System.out.println(Thread.currentThread().getName() +": "+ integerThreadLocal.get());
System.out.println(Thread.currentThread().getName() +": "+ stringThreadLocal.get());
}, "thread"+i).start();
}
}
/**
thread0: 0
thread4: 4
thread4: 偶数
thread3: 3
thread3: 奇数
thread7: 7
thread2: 2
thread2: 偶数
thread1: 1
thread8: 8
thread8: 偶数
thread9: 9
thread9: 奇数
thread7: 奇数
thread6: 6
thread6: 偶数
thread5: 5
thread5: 奇数
thread0: 偶数
thread1: 奇数
Process finished with exit code 0
*/
ThreadLocal原理
ThreadLocal在每个线程内都留有一份副本,存放在Thread类中的ThreadLocalMap中。
ThreadLocalMap threadLocals = null;
ThreadLocalMap是一个key、value的哈希表,key存放的是ThreadLocal对象,value是要保存的值。
每次访问ThreadLocal类的get()方法,会首先获取当前Thread对象,并拿到Thread对象的ThreadLocalMap。根据当前ThreadLocal对象,在ThreadLocalMap中获取相应的值。
下面的代码反映了这个过程。
public T get() {
Thread var1 = Thread.currentThread();
ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
if (var2 != null) {
ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this);
if (var3 != null) {
Object var4 = var3.value;
return var4;
}
}
return this.setInitialValue();
}
ThreadLocal.ThreadLocalMap getMap(Thread var1) {
return var1.threadLocals;
}
通过上面的了解,我绘制了ThreadLocal,Thread,ThreadLocalMap之间的关系图。
ThreadLocalMap
上面的原理中,我们知道了ThreadLocalMap存放于Thread中,每个线程获取自己的ThreadLocalMap,不会存在竞争,也不会有线程安全问题。
下面我们再详细介绍一下ThreadLocalMap,看到名字就知道这是一个Map数据结构。ThreadLocalMap使用的是线性探测再散列法解决冲突。
举个例子介绍一下线性探测再散列法。
开辟一个大小为8的数组,下标为0~7。
假设哈希函数为:x % 8,其中x为要保存的值。
假设要保存的第一个值是1,1%8=1,所以1这个数据存放在数组下标为1的位置。
要保存第二个值是2,2%8=2,所以2这个数据存放在数组下标为2的位置。
如果要保存的值是9,9%8=1,此时数组下标1的位置已经存在一个数据,并且下标1的数据是1,和9不相同,因此要想保存数据,就需要找下一个下标,也就是2。
不巧的是下标2的位置上也有数据,并且下标2中的数据2和9不同,就需要再找下一个空的位置,找到下标3,此时3里面不存在数据,9就放在了数组中下标为3的位置中。
线性探测再散列法就是不断地向下查找,直到找到数据或者存放数据。最差的性能是O(n),也就是全部查找对比一遍。
弱引用在ThreadLocal中的应用
ThreadLocalMap查找时,定位到数据后,会对比Entry的key是否相同(也就是ThreadLocal是否相同)。Entry的结构如下:
Entry是ThreadLocalMap中数组的一个对象,继承了WeakReference。其中key为弱引用,value为强引用。
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> var1, Object var2) {
super(var1);
this.value = var2;
}
}
当ThreadLocal已经被回收时(线程销毁),key不存在强引用可达,并且key是弱引用,在发生GC时就会被回收。
此时ThreadLocalMap中的key就设置为了null,但是value是强引用的
原文始发于微信公众号(Java不惑):一文带你深入了解ThreadLocal
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24515.html