使用 ThreadLocal 或 InheritableThreadLocal 对线程内部对象做缓存,实现线程内对象的复用

这篇博客介绍了Java中ThreadLocal和InheritableThreadLocal的用途,它们如何实现线程内部对象的复用以及线程安全。ThreadLocal提供线程私有的对象存储,而InheritableThreadLocal则允许子线程继承其值。通过示例展示了这两个特性,并提醒在使用后及时调用remove()方法以防止内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在java.lang.Thread类中有两个属性

ThreadLocal.ThreadLocalMap threadLocals

ThreadLocal.ThreadLocalMap inheritableThreadLocals

这里的ThreadLocalMap 类相当于一个Map,key 是 ThreadLocal 对象,value 就是存储的值。

threadLocals 里边的值是当前线程私有的,是线程安全的,threadLocals里可以存储一些对于线程安全有要求的对象,当下一次使用该线程时,threadLocals中的对象可以复用。当该线程消失之后,threadLocals 中的对象会被垃圾回收(如果对象被引用将不被回收)。

inheritableThreadLocals 中的值是可以被子线程继承的,inheritableThreadLocals 里可以存储一些允许被子线程使用的对象,下一次使用该线程并且创建子线程时,inheritableThreadLocals 中的对象可以复用。

举个栗子:

    public static void main(String[] args) {
        ThreadLocal<String> localString = new ThreadLocal<String>();
        ThreadLocal<Long> localLong = new ThreadLocal<Long>();
        ThreadLocal<String> inherLocalString = new InheritableThreadLocal <String>();
        ThreadLocal<Long> inherLocalLong = new InheritableThreadLocal <Long>();
        // 定义一个线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10, 10L, TimeUnit.SECONDS, 
                new LinkedBlockingQueue<Runnable>(512),
                Executors.defaultThreadFactory(), 
                new ThreadPoolExecutor.CallerRunsPolicy());
        // 线程的计数器
        CountDownLatch main = new CountDownLatch(12);
        // 定义多线程执行的任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                
                String name = Thread.currentThread().getName();
                long id = Thread.currentThread().getId();
                
                String myString = localString.get();
                String myInherString = inherLocalString.get();
                Long myLong = localLong.get();
                Long myInherLong = inherLocalLong.get();
                
                if (StringUtils.isBlank(myString)) {
                    myString = name;
                    localString.set(myString);
                    System.out.println(name+" 线程 localString 存储 myString:"+myString);
                }
                if (StringUtils.isBlank(myInherString)) {
                    myInherString = name;
                    inherLocalString.set(myInherString);
                    System.out.println(name+" 线程 inherLocalString 存储 myInherString:"+myInherString);
                }
                if (null == myLong) {
                    myLong = id;
                    localLong.set(myLong);
                    System.out.println(name+" 线程 localString 存储 myLong:"+myLong);
                }
                if (null == myInherLong) {
                    myInherLong = id;
                    inherLocalLong.set(myInherLong);
                    System.out.println(name+" 线程 inherLocalLong 存储 myInherLong:"+myInherLong);
                }
                System.out.println(name+" 线程的 myString:"+myString);
                System.out.println(name+" 线程的 myLong:"+myLong);
                System.out.println(name+" 线程的 myInherString:"+myInherString);
                System.out.println(name+" 线程的 myInherLong:"+myInherLong);
                
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(name+" 线程的子线程获取 myString:"+localString.get());
                        System.out.println(name+" 线程的子线程获取 myLong:"+localLong.get());
                        System.out.println(name+" 线程的子线程获取 myInherString:"+inherLocalString.get());
                        System.out.println(name+" 线程的子线程获取 myInherLong:"+inherLocalLong.get());
                        // 线程执行完毕,主线程计数器减1;
                        main.countDown();
                    }
                }).start();
            }
        };
        // 开辟12条线程执行任务
        for (int i = 0; i < 12; i++) {
            threadPool.execute(runnable);
            // 分隔符
            if (i == 3 || i == 7) {
                for (int j = 1; j <= 2100000000; j++) {
                    if (j == 2100000000) {
                        System.out.println("=================================");
                    }
                }
            }
        }
        try {
            // 主线程阻塞,等待计数器计数归零后继续执行。
            main.await();
            System.out.println("所有线程任务执行完毕,回收所有的ThreadLocal。");
            localString.remove();
            localLong.remove();
            inherLocalString.remove();
            inherLocalLong.remove();
            System.out.println("线程池不用了。关闭线程池。如果之前没有对ThreadLocal进行回收,线程池关闭后,线程里的ThreadLocal会被回收。");
            threadPool.shutdown();
        } catch (InterruptedException e) {
            // shutdown()会在线程任务都执行完毕后将线程池关闭。
            threadPool.shutdown();
        }
    }

运行结果:

pool-1-thread-2 线程 localString 存储 myString:pool-1-thread-2
pool-1-thread-2 线程 inherLocalString 存储 myInherString:pool-1-thread-2
pool-1-thread-1 线程 localString 存储 myString:pool-1-thread-1
pool-1-thread-3 线程 localString 存储 myString:pool-1-thread-3
pool-1-thread-4 线程 localString 存储 myString:pool-1-thread-4
pool-1-thread-3 线程 inherLocalString 存储 myInherString:pool-1-thread-3
pool-1-thread-1 线程 inherLocalString 存储 myInherString:pool-1-thread-1
pool-1-thread-4 线程 inherLocalString 存储 myInherString:pool-1-thread-4
pool-1-thread-2 线程 localString 存储 myLong:11
pool-1-thread-3 线程 localString 存储 myLong:12
pool-1-thread-1 线程 localString 存储 myLong:10
pool-1-thread-4 线程 localString 存储 myLong:13
pool-1-thread-1 线程 inherLocalLong 存储 myInherLong:10
pool-1-thread-3 线程 inherLocalLong 存储 myInherLong:12
pool-1-thread-3 线程的 myString:pool-1-thread-3
pool-1-thread-3 线程的 myLong:12
pool-1-thread-3 线程的 myInherString:pool-1-thread-3
pool-1-thread-2 线程 inherLocalLong 存储 myInherLong:11
pool-1-thread-3 线程的 myInherLong:12
pool-1-thread-1 线程的 myString:pool-1-thread-1
pool-1-thread-4 线程 inherLocalLong 存储 myInherLong:13
pool-1-thread-1 线程的 myLong:10
pool-1-thread-2 线程的 myString:pool-1-thread-2
pool-1-thread-1 线程的 myInherString:pool-1-thread-1
pool-1-thread-4 线程的 myString:pool-1-thread-4
pool-1-thread-1 线程的 myInherLong:10
pool-1-thread-2 线程的 myLong:11
pool-1-thread-2 线程的 myInherString:pool-1-thread-2
pool-1-thread-2 线程的 myInherLong:11
pool-1-thread-4 线程的 myLong:13
pool-1-thread-4 线程的 myInherString:pool-1-thread-4
pool-1-thread-4 线程的 myInherLong:13
pool-1-thread-4 线程的子线程获取 myString:null
pool-1-thread-4 线程的子线程获取 myLong:null
pool-1-thread-1 线程的子线程获取 myString:null
pool-1-thread-1 线程的子线程获取 myLong:null
pool-1-thread-1 线程的子线程获取 myInherString:pool-1-thread-1
pool-1-thread-1 线程的子线程获取 myInherLong:10
pool-1-thread-4 线程的子线程获取 myInherString:pool-1-thread-4
pool-1-thread-4 线程的子线程获取 myInherLong:13
pool-1-thread-3 线程的子线程获取 myString:null
pool-1-thread-2 线程的子线程获取 myString:null
pool-1-thread-2 线程的子线程获取 myLong:null
pool-1-thread-3 线程的子线程获取 myLong:null
pool-1-thread-3 线程的子线程获取 myInherString:pool-1-thread-3
pool-1-thread-2 线程的子线程获取 myInherString:pool-1-thread-2
pool-1-thread-2 线程的子线程获取 myInherLong:11
pool-1-thread-3 线程的子线程获取 myInherLong:12
=================================
pool-1-thread-2 线程的 myString:pool-1-thread-2
pool-1-thread-3 线程的 myString:pool-1-thread-3
pool-1-thread-1 线程的 myString:pool-1-thread-1
pool-1-thread-4 线程的 myString:pool-1-thread-4
pool-1-thread-1 线程的 myLong:10
pool-1-thread-3 线程的 myLong:12
pool-1-thread-2 线程的 myLong:11
pool-1-thread-2 线程的 myInherString:pool-1-thread-2
pool-1-thread-3 线程的 myInherString:pool-1-thread-3
pool-1-thread-1 线程的 myInherString:pool-1-thread-1
pool-1-thread-4 线程的 myLong:13
pool-1-thread-4 线程的 myInherString:pool-1-thread-4
pool-1-thread-1 线程的 myInherLong:10
pool-1-thread-3 线程的 myInherLong:12
pool-1-thread-2 线程的 myInherLong:11
pool-1-thread-4 线程的 myInherLong:13
pool-1-thread-1 线程的子线程获取 myString:null
pool-1-thread-1 线程的子线程获取 myLong:null
pool-1-thread-1 线程的子线程获取 myInherString:pool-1-thread-1
pool-1-thread-1 线程的子线程获取 myInherLong:10
pool-1-thread-2 线程的子线程获取 myString:null
pool-1-thread-2 线程的子线程获取 myLong:null
pool-1-thread-2 线程的子线程获取 myInherString:pool-1-thread-2
pool-1-thread-2 线程的子线程获取 myInherLong:11
pool-1-thread-3 线程的子线程获取 myString:null
pool-1-thread-3 线程的子线程获取 myLong:null
pool-1-thread-3 线程的子线程获取 myInherString:pool-1-thread-3
pool-1-thread-4 线程的子线程获取 myString:null
pool-1-thread-4 线程的子线程获取 myLong:null
pool-1-thread-4 线程的子线程获取 myInherString:pool-1-thread-4
pool-1-thread-4 线程的子线程获取 myInherLong:13
pool-1-thread-3 线程的子线程获取 myInherLong:12
=================================
pool-1-thread-1 线程的 myString:pool-1-thread-1
pool-1-thread-3 线程的 myString:pool-1-thread-3
pool-1-thread-3 线程的 myLong:12
pool-1-thread-3 线程的 myInherString:pool-1-thread-3
pool-1-thread-3 线程的 myInherLong:12
pool-1-thread-2 线程的 myString:pool-1-thread-2
pool-1-thread-2 线程的 myLong:11
pool-1-thread-2 线程的 myInherString:pool-1-thread-2
pool-1-thread-2 线程的 myInherLong:11
pool-1-thread-4 线程的 myString:pool-1-thread-4
pool-1-thread-1 线程的 myLong:10
pool-1-thread-3 线程的子线程获取 myString:null
pool-1-thread-3 线程的子线程获取 myLong:null
pool-1-thread-4 线程的 myLong:13
pool-1-thread-2 线程的子线程获取 myString:null
pool-1-thread-2 线程的子线程获取 myLong:null
pool-1-thread-2 线程的子线程获取 myInherString:pool-1-thread-2
pool-1-thread-2 线程的子线程获取 myInherLong:11
pool-1-thread-3 线程的子线程获取 myInherString:pool-1-thread-3
pool-1-thread-1 线程的 myInherString:pool-1-thread-1
pool-1-thread-1 线程的 myInherLong:10
pool-1-thread-3 线程的子线程获取 myInherLong:12
pool-1-thread-4 线程的 myInherString:pool-1-thread-4
pool-1-thread-4 线程的 myInherLong:13
pool-1-thread-1 线程的子线程获取 myString:null
pool-1-thread-1 线程的子线程获取 myLong:null
pool-1-thread-1 线程的子线程获取 myInherString:pool-1-thread-1
pool-1-thread-1 线程的子线程获取 myInherLong:10
pool-1-thread-4 线程的子线程获取 myString:null
pool-1-thread-4 线程的子线程获取 myLong:null
pool-1-thread-4 线程的子线程获取 myInherString:pool-1-thread-4
pool-1-thread-4 线程的子线程获取 myInherLong:13
所有线程任务执行完毕,回收所有的ThreadLocal。
线程池不用了。关闭线程池。如果之前没有对ThreadLocal进行回收,线程池关闭后,线程里的ThreadLocal会被回收。

可以看出线程1、2、3、4都存储了自己的myString、myLong、myInherString、myInherLong,其中myInherString、myInherLong可以被子线程使用,当再次连接线程1、2、3、4时,线程中存储的myString、myLong、myInherString、myInherLong可以被复用,myInherString、myInherLong依然可以被子线程使用。

对于一些我们希望可以被多线程复用,又担心这些对象线程不安全的情况下,可以使用ThreadLocal或InheritableThreadLocal来解决问题。例如SimpleDateFormat类的对象。

注意:自定义的ThreadLocal或InheritableThreadLocal在使用完毕后要回收。threadLocal.remove()。避免在线程池不关闭的情况下threadLocals中的对象无法回收导致内存溢出。参考博客:Java并发:ThreadLocal详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值