我理解的ThreaLocal就是内存中有一个变量,每个线程都要拿过来这个变量,但是他们自己去操作自己拿到的那一份变量,这些变量都是一个东西,但是在不同的线程下会有不同的数值,类似CS go里每个人的血量子弹等
ThreadLocal的API
在使用ThreadLocal时,我们需要先初始化一个变量,我们可以选择initialValue或者withInitial方法。我们应更多的配合Lambda表达式去使用withInitial方法,这样更方便,我们推荐用static来修饰threadlocal
/***
* 看每个销售员可以出售多少套房子
*/
class House{
//public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
//withInitial(Supplier<? extends S> supplier):创建线程局部变量
//ThreadLocal本地线程变量,线程自带的变量副本
static ThreadLocal<Integer> threadLocal=
ThreadLocal.withInitial(()->0);
public void saleHouse(){
//T get():返回当前线程的此线程局部变量的副本中的值。
Integer value = threadLocal.get();
value++;
//void set(T value):将当前线程的此线程局部变量的副本设置为指定的值。
threadLocal.set(value);
}
}
public class ThreadLocalDemo {
public static void main(String[] args) {
House house = new House();
new Thread(()->{
try{
for (int i = 1; i <=3; i++) {
house.saleHouse();
}
System.out.println(Thread.currentThread().getName()+"\t"+"卖出:"+house.threadLocal.get());
}catch (Exception e){
e.getStackTrace();
}finally {
//void remove():删除此线程局部变量的当前线程的值
//在阿里巴巴手册中有说明,尽量在代理中使用try-finally块进行回收
house.threadLocal.remove();
//下面获取到的值是线程的初始值0
System.out.println("**********"+house.threadLocal.get());
}
},"t1").start();
new Thread(()->{
try{
for (int i = 1; i <=5; i++) {
house.saleHouse();
}
System.out.println(Thread.currentThread().getName()+"\t"+"卖出:"+house.threadLocal.get());
}catch (Exception e){
e.getStackTrace();
}finally {
house.threadLocal.remove();
}
},"t2").start();
new Thread(()->{
try{
for (int i = 1; i <=8; i++) {
house.saleHouse();
}
System.out.println(Thread.currentThread().getName()+"\t"+"卖出:"+house.threadLocal.get());
}catch (Exception e){
e.getStackTrace();
}finally {
house.threadLocal.remove();
}
},"t3").start();
System.out.println(Thread.currentThread().getName()+"\t"+"卖出了:"+house.threadLocal.get());
}
}
/**
* t1 卖出:3
* t2 卖出:5
* **********0
* main 卖出了:0
* t3 卖出:8
* */
接下来是底层的一些知识
2.为什么平时推荐调用.remove方法,线程池的情况下一定要调用.remove呢
其实在很多真实的业务场景来看,其实不去remove也没有关系。
原因有下:
1、一般ThreadLocal创建出来时,一般都是强引用,而且是共享资源,这时ThreadLocalMap里面的key是不会有gc的场景的,除非创建ThreadLocal这个线程也没了。
2、ThreadLocalMap是一个Thread下面的属性,若是Thread死了,其实是对应的ThreadLocalMap都会被回收,不会有root根能索引到ThreadLocalMap里面的value的,这时value也会被回收。
但是,也要值得注意的是,现在很多线程创建都是线程池的模式,也就是说可能线程创建出来后,会被一直重复使用,不会短时间内死去(所以ThreadLocalMap指向的对象也不会gc),这时需要注意若是 ThreadLocal 对象的强引用是否有置为null的操作,或者说创建 ThreadLocal的线程死掉了。就有可能出现内存泄漏的情况。