ThreadLocal的正确使用与原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal的正确使用与原理相关的知识,希望对你有一定的参考价值。
参考技术AThreadLocal是线程Thread中属性threadLocals即ThreadLocal.ThreadLocalMap的管理者,ThreadLocal用于给每个线程操作自己线程的本地变量,通过线程私有从而保证线程安全性。
拿 get() 方法来说,线程的本地变量是存放在线程实例的属性ThreadLocalMap上的,ThreadLocalMap本质上就是一个HashMap,ThreadLocal只是一个管理者,当我们的线程需要拿到自己的本地变量时,我们直接调用ThreadLocal去get本地变量即可。
因为 get() 方法底层会先获取到当前线程,然后通过当前线程拿到他的属性值ThreadLocalMap,如果ThreadLocalMap为空,则会调用ThreadLocal的初始化方法拿到初始值返回,如果不为空,则会拿该ThreadLocal作为key去获取该线程下的ThreadLocalMap里对应的value值。
线程的属性值ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用,而value是强引用。所以,如果ThreadLocal没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而value 不会被清理掉。这样的话,ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。
因此针对这种情况,我们有两种原则:
InheritableThreadLocal类是ThreadLocal类的子类。ThreadLocal中每个线程拥有它自己的值,与ThreadLocal不同的是, InheritableThreadLocal允许一个线程以及该线程创建的所有子线程都可以访问它保存的值 。
Java--ThreadLocal原理与使用
ThreadLocal保证线程安全:
ThreadLocal内部持有ThreadLocalMap对象,线程内部单独创建副本,来保证数据隔离,但是由于ThreadLocalMap中key为弱引用,GC自动回收,但是value如果为强引用,就没法回收,就会造成内存泄露(除非线程退出)
关于垃圾回收(自动回收堆中没引用的对象空间):https://www.jianshu.com/p/23f8249886c6
参考链接:https://www.cnblogs.com/jalon/p/14819372.html
/*
* 线程安全:
* 1.synchronized修饰
* 2.ThreadLocal修饰--数据隔离
*
* */
private ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue() {
// 这里会输出10次,分别是每个线程的id
System.out.println(Thread.currentThread().getId());
return new SimpleDateFormat("yyyy-MM-dd");
}
};
public void parse2(String dateString){
try {
System.out.println(threadLocal.get().parse(dateString));
} catch (ParseException e) {
e.printStackTrace();
}
}
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
public void parse(String dateString){
try {
synchronized (simpleDateFormat){
System.out.println(simpleDateFormat.parse(dateString));
}
} catch (ParseException e) {
e.printStackTrace();
}
}
@Test
void testThreadLocal(){
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 300; i++) {
service.submit(() -> {
// parse("2021-05-30");
parse2("2021-05-30");
});
}
}
以上是关于ThreadLocal的正确使用与原理的主要内容,如果未能解决你的问题,请参考以下文章