带你读《2022技术人的百宝黑皮书》——合理使用线程池以及线程变量(14)https://developer.aliyun.com/article/1340055?groupCode=taobaotech
最佳实践
ThreadLocal变量值初始化和清理建议成对出现如果不执行清理操作,则可能会出现:
- 内存泄漏:由于ThreadLocalMap的中key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal 在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收,从而Entry里面的元素出现
<null,value>的情况。如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,这样可能会导致内存泄露。
- 脏数据:由于线程复用,在用户1请求时,可能保存了业务数据在ThreadLocal中,若不清理,则用户2的请求进来时,可能会读到用户1的数据。
ThreadLocal变量建议使用static进行修饰
我们在使用ThreadLoc al时,通常期望的语义是perThread,若不使用st at ic 进行修饰,则语义变为perThread-perInstance;在线程池场景下,若不用static进行修饰,创建的线程相关实例可能会达到 M * N个
(其中M为线程数,N为对应类的实例数),易造成内存泄漏(https://errorprone.info/bugpattern/ThreadLo- calUsage)。
谨慎使用ThreadLocal.withInitial
在应用中,谨慎使用ThreadLocal.withInitial(SupplierS> supplier)这个工厂方法创建ThreadLocal 对象,一旦不同线程的ThreadLocal使用了同一个Supplier对象,那么隔离也就无从谈起了,如:
1// 反例,实际上使用了共享对象obj而并未隔离, 2private static ThreadLocal<Obj> threadLocal = ThreadLocal.withIntitial(() -> obj);
总结
在java工程实践中,线程池和线程变量被广泛使用,因线程池和线程变量的不当使用经常造成安全生产事故,因此,正确使用线程池和线程变量是每一位开发人员必须修炼的基本功。本文从线程池和线程变量的使用出发,简要介绍了线程池和线程变量的原理和使用实践,各开发人员可结合最佳实践和实际应用场景,正确地使用线程和线程变量,构建出稳定、高效的java应用服务。
团队介绍
我们来自大淘宝技术-淘宝交易平台,负责淘宝业务的商品详情、购物车、下单、订单、物流、退款等从购前、购中到购后履约的基础链路相关业务。这里有百亿级别的数据、有超过百万QPS的高并发流量、有丰富的业务场景,服务于10亿级的消费者,支撑淘宝天猫前后端各种行业的基础业务、玩法、规则及业务拓展。这里有巨大的挑战等着你来,若有兴趣可将简历发至lican.lc@alibaba-inc.com,期待您的加入!