ThreadLocal不会难到我们吧?

大家好,我是方圆
一定要学好多线程!


1. Thread、ThreadLocal和ThreadLocalMap三者的关系

每个Thread中都有一个ThreadLocalMap对象,而ThreadLocalMap中存储的是多个ThreadLocal对象
在这里插入图片描述
其中ThreadLocalMap中的key为ThreadLocal对象,value为ThreadLocal中是我们要存储的值


2. get方法

在这里插入图片描述


3. set方法

在这里插入图片描述

3.1 createMap方法

在这里插入图片描述
在这里插入图片描述


4. remove方法

这个方法比较简单,大家过一眼就能看明白

 public void remove() {
     ThreadLocalMap m = getMap(Thread.currentThread());
     if (m != null)
         m.remove(this);
 }

4.1 重点说一说它的Value值内存泄漏问题

4.1.1 先看看Entry中强引用和弱引用

Entry为ThreadLocalMap中的一个静态内部类,我们看看源码

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  • 我们可以明确的发现value值用的是强引用,而对于key值则调用了父类的方法,我们点进去看看
    在这里插入图片描述
    在弱引用下,我们使用完ThreadLocal对象,它就会被回收,也就是说key值会为null;
    但是value就不一样了,它是强引用,会与线程的声明周期一样,线程不停止就会一直存在,这样就可能造成OOM问题

  • 引用链
    在这里插入图片描述

4.1.2 OOM问题我们需要注意的点

ThreadLocal在set方法remove方法中其实都会扫描key为null的Entry,并把对应的value也设置为null,这样就能避免了OOM问题,从而对value对象进行回收。

有了以上措施,但是还是需要我们进行方法调用才能进行清除,所以,我们自己必须要注意到这一点,而且阿里规约中也有说明:使用完ThreadLocal对象之后,应该调用remove方法,删除Entry对象,避免内存泄漏


5. ThreadLocal使用的两大场景

5.1 每个线程需要一个独享的对象

//第一种情况,每次执行date方法都会新建一个dateFormat对象,浪费空间
//第二种情况,我们把方法中的format提取出来,作为共享资源使用,但是会出现重复,造成线程不安全
//第三种情况,使用ThreadLocal,使得当前线程中都有一个私有对象使用,调用get方法获取,witInitial来添加,supplier函数式接口
//TODO 这种情况调用的是withInitial方法,因为对象的生成是由ThreadLocal控制的,而之后的set方法,对象的生成不由ThreadLocal控制
public class ThreadLocalDemo1 {
    private static ExecutorService pool = Executors.newFixedThreadPool(8);
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");


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

        for (int i = 0; i < 1000; i++) {
            int temp = i;
            pool.execute(() -> {
                System.out.println(date(temp));
            });
        }

        TimeUnit.SECONDS.sleep(1);
        pool.shutdown();
    }


    private static String date(int seconds){
        Date date = new Date(seconds * 1000);
        // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        SimpleDateFormat simpleDateFormat =
                ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).get();
        return simpleDateFormat.format(date);
    }
}

class ThreadLocalSafeSimpleDateFormat {

    public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
}

5.2 每个线程需要保存全局变量

//TODO 这个使用场景区别于上一个,首先方法用的是set,因为存储对象不由ThreadLocal控制
//其次呢,这个场景重要是应用在多个服务要调用一个参数时,避免重复传递,直接由ThreadLocal获取
//它的好处:线程安全;避免传参的麻烦
public class ThreadLocalDemo2 {
    public static void main(String[] args) {
        new Service1().doService();
    }

}

class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

class UserContextHolder {
    public static ThreadLocal<User> userHolder = new ThreadLocal<>();
}

class Service1 {

    public void doService() {
        User user = new User("小腚腚");
        UserContextHolder.userHolder.set(user);
        System.out.println("小腚腚在服务1中加入了");
        new Service2().doService();
    }
}
class Service2 {

    public void doService() {
        User user = UserContextHolder.userHolder.get();
        System.out.println(user.toString() + "在服务2中努力学习ThreadLocal");
        new Service3().doService();
    }
}
class Service3 {

    public void doService() {
        User user = UserContextHolder.userHolder.get();
        System.out.println(user.toString() + "在服务3中努力刷LeetCode");
    }
}

加油儿!!!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

方圆想当图灵

嘿嘿,小赏就行,不赏俺也不争你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值