ThreadLocal详解 ----- 两大使用场景

典型场景1

  • 典型场景1:每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random)

针对场景1下面来模拟下2个线程分别用自己的SimpleDateFormat

public class ThreadLocalNormalUsage00 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                final String date = new ThreadLocalNormalUsage00().date(10);
                System.out.println(date);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                final String date = new ThreadLocalNormalUsage00().date(1007);
                System.out.println(date);
            }
        }).start();
    }
    public String date(int seconds){//日期转换
        //参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
        final Date date = new Date(1000 * seconds);
        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        return dateFormat.format(date);
    }
}

在这里插入图片描述

假设线程池中有10个线程,理想状态下用10个线程执行1000个任务,同时避免synchronized带来的性能损耗,因此可以给每个线程内部使用一个df(dateformat缩写),10个线程创建10个df对象,避免了性能损耗,同时也避免了synchronized带来的一直等待排队问题,同时它也是线程安全的。

public class ThreadLocalNormalUsage01 {
    public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int finalI =i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                     String date = ThreadLocalNormalUsage01.date(finalI);
                    System.out.println(date);
                }
            });
        }
        threadPool.shutdown();
    }
    public static String date(int seconds){//日期转换
        //参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
         Date date = new Date(1000 * seconds);
//        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
         SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
        return dateFormat.format(date);
    }
    static class ThreadSafeFormatter{
        public  static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() ->//初始化
           new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));//在每个线程中只有一份
    }
}

在这里插入图片描述
可以看到没有两个时间是重复的,并没有发生线程安全问题,成功利用ThreadLocal来达到这一目的。

典型场景2

  • 典型场景2:每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不太方法直接使用,避免参数传递的麻烦

实例: 当前用户信息需要被线程内所有方法共享

  • 方法1:当多线程同时工作时,我们需要保证线程安全,可以用synchronized,也可以ConcurrentHashMap,但无论用什么,都会对性能有所影响
  • 方法2:使用ThreadLocal,这样无需synchronized,可以在不影响性能的情况下,也无需层层传递参数,就可以达到保存当前线程对应的用户信息的目的

方法2明显更好,它强调同一个请求内(同一个线程内)不同方法间的共享,相比方法1,不需要重写initialValue()方法,但是必须手动调用set()方法。

下面进行代码演示

public class ThreadLocalNormalUsage02 {
    public static void main(String[] args) {
        new Serivce1().process();
    }
}
class Serivce1{
    public void process(){
        User user = new User("超哥");
        UserContextHolder.holder.set(user);
        new Service2().process();
    }
}
class Service2{
    public void process(){
        final User user = UserContextHolder.holder.get();
        System.out.println("Service2拿到用户名:"+user.name);
        new Service3().process();
    }
}
class Service3{
    public void process(){
        final User user = UserContextHolder.holder.get();
        System.out.println("Service3拿到用户名:"+user.name);
    }
}
class UserContextHolder{
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User{
    public String name;
    public User(String name) {
        this.name = name;
    }
}

在这里插入图片描述

总结

ThreadLocal的两个作用

  1. 让某个需要用到的对象在线程间隔离(每个线程都有自己的独立的对象)
  2. 在任何方法中都可以轻松获取到该对象

根据共享对象生成时机的不同来选择initialValue还是set。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

༄༊心灵骇客༣

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值