18.3.7 ThreadLocal原理与内存泄漏问题

1. ThreadLocal基础概念

1.1 ThreadLocal简介

public class ThreadLocalBasics {
    
    /*
     * ThreadLocal作用:
     * 为每个线程提供独立的变量副本,线程间互不影响
     * 
     * 核心特点:
     * 1. 线程隔离:每个线程有自己的变量副本
     * 2. 无需同步:避免线程安全问题
     * 3. 生命周期:与线程生命周期绑定
     */
    
    private static ThreadLocal<String> threadLocalValue = new ThreadLocal<>();
    private static ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0; // 设置初始值
        }
    };
    
    public void demonstrateBasicUsage() {
        // 创建多个线程,每个线程设置不同的值
        for (int i = 1; i <= 3; i++) {
            final int threadId = i;
            
            Thread thread = new Thread(() -> {
                // 每个线程设置自己的值
                threadLocalValue.set("线程" + threadId + "的值");
                threadLocalCounter.set(threadId * 10);
                
                // 模拟一些操作
                doSomeWork();
                
                // 读取值
                System.out.println(Thread.currentThread().getName() + 
                                 " - ThreadLocal值: " + threadLocalValue.get() + 
                                 ", 计数器: " + threadLocalCounter.get());
                
                // 清理资源
                threadLocalValue.remove();
                threadLocalCounter.remove();
                
            }, "Worker-" + threadId);
            
            thread.start();
        }
    }
    
    private void doSomeWork() {
        try {
            Thread.sleep(1000);
            
            // 在方法中也能访问到ThreadLocal的值
            String value = threadLocalValue.get();
            Integer counter = threadLocalCounter.get();
            
            System.out.println(Thread.currentThread().getName() + 
                             " 工作中 - 值: " + value + ", 计数器: " + counter);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

2. ThreadLocal实现原理

2.1 内部数据结构

public class ThreadLocalInternals {
    
    /*
     * ThreadLocal实现原理:
     * 
     * 1. 每个Thread对象都有一个ThreadLocalMap字段
     * 2. ThreadLocalMap是ThreadLocal的内部类
     * 3. ThreadLocalMap使用ThreadLocal对象作为key
     * 4. 实际数据存储在当前线程的ThreadLocalMap中
     * 
     * 关键数据结构:
     * Thread.threadLocals -> ThreadLocalMap
     * ThreadLocalMap.table -> Entry[]
     * Entry extends WeakReference<ThreadLocal<?>>
     */
    
    public void demonstrateInternalStructure() {
        ThreadLocal<String> tl1 = new ThreadLocal<>();
        ThreadLocal<Integer> tl2 = new ThreadLocal<>();
        
        Thread thread = new Thread(() -> {
            // 设置多个ThreadLocal值
            tl1.set("字符串值");
            tl2.set(42);
            
            System.out.println("ThreadLocal1: " + tl1.get());
            System.out.println("ThreadLocal2: " + tl2.get());
            
            // 清理
            tl1.remove();
            tl2.remove();
            
        }, "InternalThread");
        
        thread.start();
    }
}

2.2 get/set实现原理

public class ThreadLocalImplementation {
    
    /*
     * ThreadLocal核心方法实现原理:
     * 
     * set(T value):
     * 1. 获取当前线程
     * 2. 获取线程的ThreadLocalMap
     * 3. 如果map存在,设置值;否则创建map
     * 
     * get():
     * 1. 获取当前线程
     * 2. 获取线程的ThreadLocalMap
     * 3. 从map中根据ThreadLocal对象获取值
     * 4. 如果没有值,返回初始值
     */
    
    // 模拟ThreadLocal实现
    static class CustomThreadLocal<T> {
        private final Map<Thread, T> threadMap = new ConcurrentHashMap<>();
        
        public void set(T value) {
            Thread currentThread = Thread.currentThread();
            threadMap.put(currentThread, value);
            System.out.println("CustomThreadLocal.set() - 线程: " + currentThread.getName() + 
                             ", 值: " + value);
        }
        
        public T get() {
            Thread currentThread = Thread.currentThread();
            T value = threadMap.get(currentThread);
            System.out.println("CustomThreadLocal.get() - 线程: " + currentThread.getName() + 
                             ", 值: " + value);
            return value;
        }
        
        public void remove() {
            Thread currentThread = Thread.currentThread();
            threadMap.remove(currentThread);
            System.out.println("CustomThreadLocal.remove() - 线程: " + currentThread.getName());
        }
    }
}

3. 内存泄漏问题分析

3.1 内存泄漏产生原因

public class ThreadLocalMemoryLeak {
    
    /*
     * ThreadLocal内存泄漏原因:
     * 
     * 1. ThreadLocalMap的Entry继承WeakReference<ThreadLocal<?>>
     * 2. key是弱引用,但value是强引用
     * 3. 当ThreadLocal对象被GC回收后,key变为null
     * 4. 但value仍然被Entry强引用,无法回收
     * 5. 如果线程长期存活,会导致内存泄漏
     * 
     * 引用链:
     * Thread -> ThreadLocalMap -> Entry -> value (强引用)
     */
    
    public void demonstrateMemoryLeak() {
        // 创建长期存活的线程
        Thread longLivedThread = new Thread(() -> {
            // 创建大量ThreadLocal对象
            for (int i = 0; i < 100; i++) {
                ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
                
                // 设置大对象
                byte[] largeObject = new byte[1024 * 100]; // 100KB
                threadLocal.set(largeObject);
                
                // ThreadLocal对象超出作用域,可能被GC
                // 但value仍然被强引用,造成内存泄漏
            }
            
            System.out.pri

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务