Java中ThreadLocal浅析

ThreadLocal是线程内的全局上下文。就是在单个线程中,方法之间共享的内存,每个方法都可以从该上下文中获取值和修改值。

在业务开发中,ThreadLocal 有两种典型的使用场景

  • ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
  • ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念。

场景1

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                String data = new Demo().date(finalI);
                System.out.println(data);
            }).start();
            Thread.sleep(100);
        }
    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
        return simpleDateFormat.format(date);
    }

打印

00:00
00:01
00:02
00:03
00:04
00:05
00:06
00:07
00:08
00:09

给每个线程都创建了SimpleDateFormat对象,他们之间互不影响,代码是可以正常执行的。

如果有1000个线程都用到SimpleDateFormat对象呢,对这种场景,ThreadLocal再合适不过了,ThreadLocal给每个线程维护一个自己的simpleDateFormat对象,这个对象在线程之间是独立的,互相没有关系的。这也就避免了线程安全问题。与此同时,simpleDateFormat对象还不会创造过多,线程池一共只有 16 个线程,所以需要16个对象即可。

public static ExecutorService threadPool = Executors.newFixedThreadPool(16);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                String data = new Demo().date(finalI);
                System.out.println(data);
            });
        }
        threadPool.shutdown();
    }

    private String date(int seconds){
        Date date = new Date(1000 * seconds);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
        ThreadLocalUtil.set("dateFormat",simpleDateFormat);
        SimpleDateFormat dateFormat = ThreadLocalUtil.get("dateFormat");
        return dateFormat.format(date);
    }

场景2

传统方式我们要在方法中访问某个变量,可以通过传参的形式往方法中传参,如果多个方法都要使用那么每个方法都要传参;如果使用ThreadLocal所有方法就不需要传该参数了,每个方法都可以通过ThreadLocal来访问该值。

public class ThreadLocalDemo {

    public static void main(String[] args) throws InterruptedException {
        //User user = new User("曹操");
        //new Service1().service1(user);

        for (int i = 0; i < 10; i++) {
            int m = i;
            new Thread(() -> {
                User user = new User("曹操" + m);
                new Service1().service1(user);
            }).start();
            Thread.sleep(100);
        }
    }

}

class Service1 {
    public void service1(User user){
        //给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了
        ThreadLocalUtil.set("user",user);
        new Service2().service2();
    }
}

class Service2 {
    public void service2(){
        User user = ThreadLocalUtil.get("user");
        System.out.println("service2拿到的用户:"+user.name);
        new Service3().service3();
    }
}

class Service3 {
    public void service3(){
        User user = ThreadLocalUtil.get("user");
        System.out.println("service3拿到的用户:"+user.name);
        //在整个流程执行完毕后,一定要执行remove
        ThreadLocalUtil.remove();
    }
}

class User {
    String name;
    public User(String name){
        this.name = name;
    }
}
public class ThreadLocalUtil<T> {

    private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal() {
        @Override
        protected Map<String, Object> initialValue() {
            return new HashMap<>(4);
        }
    };

    public static Map<String, Object> getThreadLocal(){
        return threadLocal.get();
    }

    public static <T> T get(String key) {
        Map map = (Map)threadLocal.get();
        return (T)map.get(key);
    }

    public static <T> T get(String key,T defaultValue) {
        Map map = (Map)threadLocal.get();
        return (T)map.get(key) == null ? defaultValue : (T)map.get(key);
    }

    public static void set(String key, Object value) {
        Map map = (Map)threadLocal.get();
        map.put(key, value);
    }

    public static void set(Map<String, Object> keyValueMap) {
        Map map = (Map)threadLocal.get();
        map.putAll(keyValueMap);
    }

    public static void remove() {
        threadLocal.remove();
    }

    public static <T> T remove(String key) {
        Map map = (Map)threadLocal.get();
        return (T)map.remove(key);
    }

    public static <T> Map<String,T> fetchVarsByPrefix(String prefix) {
        Map<String,T> vars = new HashMap<>();
        if( prefix == null ){
            return vars;
        }
        Map map = (Map)threadLocal.get();
        Set<Map.Entry> set = map.entrySet();
        for( Map.Entry entry : set){
            Object key = entry.getKey();
            if( key instanceof String ){
                if( ((String) key).startsWith(prefix) ){
                    vars.put((String)key,(T)entry.getValue());
                }
            }
        }
        return vars;
    }

    public static void clear(String prefix) {
        if( prefix == null ){
            return;
        }
        Map map = (Map)threadLocal.get();
        Set<Map.Entry> set = map.entrySet();
        List<String> removeKeys = new ArrayList<>();
        for( Map.Entry entry : set ){
            Object key = entry.getKey();
            if( key instanceof String ){
                if( ((String) key).startsWith(prefix) ){
                    removeKeys.add((String)key);
                }
            }
        }
        for( String key : removeKeys ){
            map.remove(key);
        }
    }
}

打印

service2拿到的用户:曹操0
service3拿到的用户:曹操0
service2拿到的用户:曹操1
service3拿到的用户:曹操1
service2拿到的用户:曹操2
service3拿到的用户:曹操2
service2拿到的用户:曹操3
service3拿到的用户:曹操3
service2拿到的用户:曹操4
service3拿到的用户:曹操4
service2拿到的用户:曹操5
service3拿到的用户:曹操5
service2拿到的用户:曹操6
service3拿到的用户:曹操6
service2拿到的用户:曹操7
service3拿到的用户:曹操7
service2拿到的用户:曹操8
service3拿到的用户:曹操8
service2拿到的用户:曹操9
service3拿到的用户:曹操9

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocalJava的一个类,用于在多线程环境下实现线程本地变量。它提供了一种线程级别的数据隔离机制,使得每个线程都可以拥有自己独立的变量副本,互不干扰。每个线程都可以通过ThreadLocal对象来访问自己的变量副本,而不会影响其他线程的副本。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JavaThreadLocal详解](https://blog.csdn.net/licux/article/details/117292777)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [JavaThreadLocal详解(一篇就够了)](https://blog.csdn.net/qq_38721537/article/details/124565091)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [JavaThreadLocal详解](https://blog.csdn.net/qq_53729147/article/details/127967751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值