多线程07--ThreadLocal

上一篇:https://blog.csdn.net/fengxianaa/article/details/124427303

1. 简单使用

/**
 * ThreadLocal
 *  顾名思义:线程本地变量,实现线程之间的数据隔离,所属线程game over ,则设置的变量也会回收
 *
 */
public class ThreadLocal01 {

    static ThreadLocal myThreadLocal = new ThreadLocal();

    public static void main(String[] args) throws InterruptedException {

        Thread t = new Thread(() -> {
            myThreadLocal.set("feng");//这时,"feng" 跟当前线程关联,属于当前线程的私有变量,只能在当前线程中获取到
            System.out.println(Thread.currentThread().getName()+"----"+myThreadLocal.get());
            ThreadHelper.sleep(2000);
        });
        t.start();

        System.out.println(myThreadLocal.get());// main 线程中拿不到
    }
}

2. 使用场景

/**
 * 使用场景
 *  1. 参数传递
 *
 */
public class ThreadLocal02 {

    void save(Object Person){
        /**
         *  TODO 该方法业务中,要用到当前登陆用户(currentUser),如果加参数,不合适,原因:
         *      1. 在微服务架构下,接口是对外的,如果随意修改,可能导致外部服务报错
         *      2. 如果加参数,会导致方法有歧义
         *      3. 如果该service中很多方法都需要currentUser,那么都加,明显不现实
         *
         * TODO 这时就可以用Threadlocal,在拦截器层,把currentUser设置到ThreadLocal中,
         *
         */
    }
}

3. ThreadLocalMap结构

ThreadLocal 本质是操作当前线程的 theadlocals 属性,它是 ThreadLocalMap 类型

ThreadLocalMap结构:存储 Entry 数组,数组中的元素有两个属性:referent、value

  • referent :是 ThreadLocal 对象
  • value:自己调用 ThreadLocal 的 set 方法,设置的值

TheadLocal跟threadLocals的关系

这时候可以看到myThreadLocal变量指向的对象是672

查看当前线程的threadLocals属性

可以看到,最里层有个referent属性也是指向了672

扫描二维码关注公众号,回复: 14141956 查看本文章

其实set本质:把myThreadLocal对象和feng,封装成一个Entry对象,然后放到当前线程的threadLocals属性中

4. 原理

/**
 * ThreadLocal 原理
 *
 */
public class ThreadLocal03 {

    static ThreadLocal myThreadLocal = new ThreadLocal();

    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            myThreadLocal.set("feng");//这时,"feng" 跟当前线程关联,属于当前线程的私有变量,只能在当前线程中获取到
            System.out.println(Thread.currentThread().getName()+"----"+myThreadLocal.get());
        });
        t.start();


        /**
         * TODO
         *  set 方法介绍,本质上操作的是当前线程对象的threadLocals属性
         *      1. 获取 currentThread 的 threadLocals 属性,是 ThreadLocalMap 类型
         *      2. threadLocals 是否为null
         *          是:new ThreadLocalMap(myThreadLocal,"feng")
         *                  在 ThreadLocalMap 的构造方法中,创建了一个长度是16的Entry数组,并赋值给 table 属性
         *                  利用 myThreadLocal 的 threadLocalHashCode 属性计算出一个下标 i
         *                  table[i] = new Entry(myThreadLocal, "feng");
         *                  Entry 是弱引用
         *          否:利用 myThreadLocal 的 threadLocalHashCode 属性计算出一个下标 i
         *              取出对应下标中的Entry对象,然后entry.value="feng"
         *
         */

    }
}

5. 引用类型

/**
 * ThreadLocal 
 *
 */
public class ThreadLocal03 {

    static ThreadLocal myThreadLocal = new ThreadLocal();

    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            myThreadLocal.set("feng");//这时,"feng" 跟当前线程关联,属于当前线程的私有变量,只能在当前线程中获取到
            System.out.println(Thread.currentThread().getName()+"----"+myThreadLocal.get());
        });
        t.start();


        /**
         * TODO
         *     强:一般 A a = new A() ,就是强
         *     弱:WeakReference ,gc发生时就会被回收
         *     软:SoftReference,A a = new A();SoftReference<A> soft = new SoftReference<>(a);
         *             soft 有可能会被gc回收,但是在发生OOM之前,会被回收
         *              一些有用但并非必须的对象可用软引用
         *     虚:PhantomReference,又称之为幽灵引用,被回收时会收到一个通知
         *
         */

    }
}

6. 内存泄漏

调用set方法后,设置 myThreadLocal=null 且 currentThread 不结束

这时,无法访问 currentThread 的 threadLocals 属性,设置的值也永远拿不到,还一直在内存中

/**
 * ThreadLocal 
 *
 */
public class ThreadLocal03 {

    static ThreadLocal myThreadLocal = new ThreadLocal();

    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            myThreadLocal.set("feng");//这时,"feng" 跟当前线程关联,属于当前线程的私有变量,只能在当前线程中获取到
            System.out.println(Thread.currentThread().getName()+"----"+myThreadLocal.get());
        });
        t.start();


        /**
         * TODO 内存泄漏
         *      调用set方法后,myThreadLocal=null 且 currentThread 不结束,
         *      这时,无法访问 currentThread 的 threadLocals 属性,设置的值也永远拿不到,还一直在内存中
         */

    }
}

把myThreadLocal设置为null后,可以看到referent没有什么变化

但是在执行 System.gc(); 这句代码后

referent变成null,那么这时候,feng这个值,就再也拿不到了

当然,当前线程结束后 feng 这个值还是会被回收的

猜你喜欢

转载自blog.csdn.net/fengxianaa/article/details/124427373