私人地方,禁止闯入!—— ThreadLocal

45 阅读2分钟

大家好,我是徒手敲代码。

今天来介绍一下ThreadLocal

专业解释:ThreadLocal是为了确保线程安全,提供线程间数据隔离而设计的一种机制。它允许每个线程拥有各自独立的变量副本,即使变量名相同,不同线程之间的变量值也不会相互影响。底层基于一个内部类ThreadLocalMap(一个弱引用的Entry数组),数组当中,以ThreadLocal实例为 key,存储任何类型的对象作为 value。多线程环境下,帮助每个线程维持独有的任务执行环境,或者上下文信息,比如用户会话、事务 id 等等。

通俗一点,假设商场的客人就是一个个的线程,那么放在储物柜的东西,就是每个线程各自的资源。客人 A 不能去拿客人 B 的东西,就像线程 1 不能去访问线程 2 的数据一样。

既然是每个线程都保存一份,那么如果线程数一旦多了起来,或者保存的数据占用空间很大,而且用完之后没有及时释放,肯定会造成严重的内存泄漏

针对这个问题,threadLocal主要采用弱引用 + 手动调用remove()方法的思路来解决。内存泄漏主要是因为用完的对象没有被及时地进行垃圾回收,那么我们在使用它的过程中,一旦这个threadLocal对应的对象没有用了,那么就要及时调用remove()方法,不要有半点犹豫,真正做到用完即弃。

下面写一个使用threadLocal的演示Demo:

public class ThreadLocalExample {
    // 静态ThreadLocal变量,用于演示线程隔离
    private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        System.out.println("线程开始执行。。。");

        // 创建并启动两个线程
        Thread thread1 = new Thread(new Worker(), "Thread-1");
        Thread thread2 = new Thread(new Worker(), "Thread-2");
        thread1.start();
        thread2.start();

        // 确保两个线程执行完毕再结束程序(非必须,仅为观察输出)
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("所有线程执行完毕");
    }

    static class Worker implements Runnable {
        @Override
        public void run() {
            // 每个线程生成一个随机数并存储到各自的ThreadLocal中
            int randomValue = new Random().nextInt(100);
            threadLocal.set(randomValue);

            // 打印当前线程名和其对应的ThreadLocal中的值,体现线程隔离
            System.out.println("线程: " + Thread.currentThread().getName() +
                    ", 随机数: " + threadLocal.get());

            // 模拟其他操作后更新ThreadLocal的值
            try {
                Thread.sleep(500); // 减少等待时间以便更快地观察输出
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 更新ThreadLocal的值并再次打印,证明是线程独有的
            randomValue = new Random().nextInt(100);
            threadLocal.set(randomValue);
            System.out.println("线程: " + Thread.currentThread().getName() +
                    ", 更新的随机数: " + threadLocal.get());

            // 使用remove防止内存泄漏,特别是在不再需要ThreadLocal变量时
            threadLocal.remove();
        }
    }
}

今天的分享到这里结束了。

关注公众号“徒手敲代码”,免费领取腾讯大佬推荐的Java电子书!