ThreadLocal

        ThreadLocal 是 Java 中用于实现线程本地存储的工具类,它能够让每个线程拥有自己独立的变量副本,从而避免多线程并发访问时的线程安全问题。

核心原理:

         ThreadLocal 的核心思想是:每个线程(Thread)内部维护一个 ThreadLocalMap,该 Map 以 ThreadLocal 对象为键,以线程私有变量为值。

这样每个线程访问 ThreadLocal 时,实际上是在操作自己线程内部的 Map,从而实现线程隔离。

    Thread {
        ThreadLocal.ThreadLocalMap threadLocals = null;
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
        private void exit() {
            if (threadLocals != null && TerminatingThreadLocal.REGISTRY.isPresent()) {
                TerminatingThreadLocal.threadTerminated();
            }
            threadLocals = null;
            inheritableThreadLocals = null;
        }
    }

使用注意:

        哈希冲突解决

                                ThreadLocalMap 使用线性探测法解决哈希冲突,而不是 HashMap 中的链地址法。当发生哈希冲突时,它会依次向后查找下一个空位置。

        内存泄漏问题

                        虽然 Entry 的 key 是弱引用,但 value 是强引用。

                        如果 ThreadLocal 对象被回收,而线程仍然存活,那么 value 就会成为遗漏对象,导致内存泄漏。

        解决办法:

                 1.使用完 ThreadLocal 后,及时调用 remove () 方法清除数据

                 2.避免使用静态的 ThreadLocal,因为静态变量的生命周期较长

总结:

            ThreadLocal 通过为每个线程维护独立的变量副本,优雅地解决了多线程并发访问的线程安全问题。

            其核心是 ThreadLocalMap 数据结构,它使用线性探测法解决哈希冲突,并通过弱引用机制尝试避免内存泄漏。

            ThreadLocal 时,应注意及时清理资源,避免内存泄漏问题。常见的应用场景包括数据库连接管理、Session 管理等需要线程隔离的场景。

使用示例:

    典型使用场景

        1.用户身份信息传递:在请求处理链路中传递用户 ID、权限等信息

        2.分布式追踪:传递请求唯一标识(Trace ID)用于日志追踪

        3.事务管理:存储当前线程的事务信息

        4.多租户系统:传递租户标识,实现数据隔离

    代码说明

        1.UserContext:封装了 ThreadLocal 的操作,提供了设置、获取和清除用户信息的静态方法。这是最佳实践,避免直接操作 ThreadLocal 实例。

        2.UserContextInterceptor:拦截器在请求开始时(preHandle)设置用户信息到 ThreadLocal,在请求完成后(afterCompletion)

            清除 ThreadLocal 中的数据,这是防止内存泄漏的关键。

        3.WebConfig:注册拦截器,使其对所有请求生效。

        4.UserController 和 UserService:展示了在控制器和服务层如何方便地获取用户信息,无需通过方法参数传递,简化了代码。

    注意事项

        1.必须清除 ThreadLocal:使用完 ThreadLocal 后一定要调用remove()方法清除数据,特别是在使用线程池的环境下,否则线程复用会导致数据泄露。

        2.避免存储大量数据:ThreadLocal 中存储的数据会随线程生命周期存在,存储大量数据可能导致内存问题。

        3.不能用于跨线程传递数据:ThreadLocal 只在当前线程有效,无法在父线程和子线程之间传递数据(如需此功能可使用 InheritableThreadLocal)

        4.谨慎使用静态方法:示例中使用了静态方法简化调用,在某些情况下也可以使用实例方法并通过依赖注入使用。

源码:

UserContext.Java

           /**
            * 用户上下文工具类,用于在同一线程中传递用户信息
            */
            @Component
            public class UserContext {
                
                // 线程局部变量,存储当前线程的用户ID
                private static final ThreadLocal<Long> currentUserId = new ThreadLocal<>();
                
                // 线程局部变量,存储当前线程的用户名
                private static final ThreadLocal<String> currentUsername = new ThreadLocal<>();
                
                // 设置用户ID
                public static void setUserId(Long userId) {
                    currentUserId.set(userId);
                }
                
                // 获取用户ID
                public static Long getUserId() {
                    return currentUserId.get();
                }
                
                // 设置用户名
                public static void setUsername(String username) {
                    currentUsername.set(username);
                }
                
                // 获取用户名
                public static String getUsername() {
                    return currentUsername.get();
                }
                
                // 清除线程局部变量,非常重要,避免内存泄漏
                public static void clear() {
                    currentUserId.remove();
                    currentUsername.remove();
                }
            }

UserContextInterceptor.Java

            /**
            * 请求拦截器,用于在请求开始时设置用户上下文,请求结束时清除
            */
            public class UserContextInterceptor implements HandlerInterceptor {
                
                @Override
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
                    // 实际应用中,这里通常从token或session中解析用户信息
                    // 这里为了示例直接模拟一个用户
                    Long userId = 1001L;
                    String username = "testUser";
                    
                    // 设置用户上下文
                    UserContext.setUserId(userId);
                    UserContext.setUsername(username);
                    return true;
                }
                
                @Override
                public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                                        Object handler, Exception ex) {
                    // 清除用户上下文,避免线程池复用导致的信息泄露
                    UserContext.clear();
                }
            }

UserController.Java

            /**
            * 用户控制器,演示如何使用ThreadLocal传递的用户上下文
            */
            @RestController
            public class UserController {
                
                private final UserService userService;
                
                // 构造函数注入
                public UserController(UserService userService) {
                    this.userService = userService;
                }
                
                @GetMapping("/user/info")
                public String getUserInfo() {
                    // 直接从ThreadLocal中获取用户信息
                    return userService.getUserInfo();
                }
            }

UserService.Java

            /**
            * 用户服务类,演示在服务层获取ThreadLocal中的用户信息
            */
            @Service
            public class UserService {
                
                public String getUserInfo() {
                    // 在服务层获取ThreadLocal中的用户信息,无需通过方法参数传递
                    Long userId = UserContext.getUserId();
                    String username = UserContext.getUsername();
                    
                    return "当前用户ID: " + userId + ", 用户名: " + username;
                }
            } 

WebConfig.Java

            /**
             * Web配置类,注册拦截器
             */
            @Configuration
            public class WebConfig implements WebMvcConfigurer {
                
                @Override
                public void addInterceptors(InterceptorRegistry registry) {
                    // 注册用户上下文拦截器,对所有请求生效
                    registry.addInterceptor(new UserContextInterceptor()).addPathPatterns("/**");
                }
            }  

类结构

import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

/*
 * 此类提供线程局部变量。这些变量与普通变量的不同之处在于:
 *      每个通过其get(获取)或set(设置)方法访问该变量的线程,都拥有独立初始化的变量副本。
 *      ThreadLocal 实例通常是类中的私有静态字段,用于将状态(例如用户 ID、事务 ID)与线程相关联。
 *      例如,下面的类会为每个线程生成独有的局部标识符。
 *      线程的 ID 在其首次调用ThreadId.get()方法时分配,后续调用中保持不变。
 */
public class ThreadLocal<T> {
    // 哈希码计算相关
    private final int threadLocalHashCode = nextHashCode();

    private static AtomicInteger nextHashCode = new AtomicInteger();

    private static final int HASH_INCREMENT = 0x61c88647;

    private static int nextHashCode() {
        //ThreadLocal 使用一种特殊的哈希码计算方式,通过 HASH_INCREMENT(0x61c88647)这个魔数来保证哈希码的均匀分布:
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

    protected T initialValue() {
        return null;
    }

    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

    public ThreadLocal() {
    }

    public T get() {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                T result = (T)e.value;
                return result;
            }
        }
        // 如果map不存在或没有对应的Entry,初始化
        return setInitialValue();
    }

    boolean isPresent() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        return map != null && map.getEntry(this) != null;
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

    public void remove() {
        //remove () 方法用于移除当前线程对应的变量副本
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null) {
             m.remove(this);
         }
     }

     ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

    T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

    static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }




    static class ThreadLocalMap {
        //Entry 类继承了 WeakReference<ThreadLocal<?>>,这是为了在 ThreadLocal 对象不再被引用时能够被垃圾回收,避免内存泄漏:
        //虽然 Entry 的 key 是弱引用,但 value 是强引用。
        //如果 ThreadLocal 对象被回收,而线程仍然存活,那么 value 就会成为孤儿对象,导致内存泄漏。
        //1.使用完 ThreadLocal 后,及时调用 remove () 方法清除数据
        //2.避免使用静态的 ThreadLocal,因为静态变量的生命周期较长
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private static final int INITIAL_CAPACITY = 16;
        //ThreadLocalMap 使用数组存储 Entry 对象
        private Entry[] table;

        private int size = 0;

        private int threshold; // Default to 0

        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }

        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

        private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值