Bootstrap

ThreadLocal学习笔记

关于什么是ThreadLocal以及他的用处

我理解的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
 * */

接下来是底层的一些知识

1.四大引用都是什么

2.为什么平时推荐调用.remove方法,线程池的情况下一定要调用.remove呢

其实在很多真实的业务场景来看,其实不去remove也没有关系。
原因有下:
1、一般ThreadLocal创建出来时,一般都是强引用,而且是共享资源,这时ThreadLocalMap里面的key是不会有gc的场景的,除非创建ThreadLocal这个线程也没了。
2、ThreadLocalMap是一个Thread下面的属性,若是Thread死了,其实是对应的ThreadLocalMap都会被回收,不会有root根能索引到ThreadLocalMap里面的value的,这时value也会被回收。

但是,也要值得注意的是,现在很多线程创建都是线程池的模式,也就是说可能线程创建出来后,会被一直重复使用,不会短时间内死去(所以ThreadLocalMap指向的对象也不会gc),这时需要注意若是 ThreadLocal 对象的强引用是否有置为null的操作,或者说创建 ThreadLocal的线程死掉了。就有可能出现内存泄漏的情况。

           

;