30 | 线程本地存储模式:没有共享,就没有伤害
该思维导图由 AI 生成,仅供参考
ThreadLocal 的使用方法
- 深入了解
- 翻译
- 解释
- 总结
线程本地存储模式是解决并发问题的重要方法,其核心思想是避免共享变量,从而避免并发问题的发生。文章通过引人入胜的校庆篮球场的段子引出了避免共享的重要性,介绍了线程封闭和局部变量的方式来避免共享,并详细讲解了Java语言提供的线程本地存储(ThreadLocal)的使用方法和工作原理。通过示例代码和图示,阐述了ThreadLocal的内部实现机制,以及在线程池中使用ThreadLocal可能导致的内存泄露问题。此外,还介绍了InheritableThreadLocal的继承性特点,但不建议在线程池中使用。总体来说,本文深入浅出地介绍了线程本地存储模式的原理和应用,对于并发编程领域的读者具有很高的参考价值。 线程本地存储模式本质上是一种避免共享的方案,由于没有共享,所以自然也就没有并发问题。如果需要在并发场景中使用一个线程不安全的工具类,最简单的方案就是避免共享。避免共享有两种方案,一种是将工具类作为局部变量使用,另一种是线程本地存储模式。线程本地存储模式是解决并发问题的常用方案,所以Java SDK也提供了相应的实现:ThreadLocal。然而,在线程池中使用ThreadLocal仍可能导致内存泄漏,因此使用ThreadLocal需要谨慎。在实际工作中,许多平台型的技术方案都采用ThreadLocal来传递上下文信息,例如Spring使用ThreadLocal来传递事务信息。在异步场景中,是否可以使用Spring的事务管理器呢?这是一个值得思考的问题。 总的来说,本文深入探讨了线程本地存储模式的原理和应用,对于并发编程领域的读者具有很高的参考价值。
《Java 并发编程实战》,新⼈⾸单¥59
全部留言(57)
- 最新
- 精选
- 右耳听海有个疑问请教老师,避免共享变量的两种解决方案,在高并发情况下,使用局部变量会频繁创建对象,使用threadlocal也是针对线程创建新变量,都是针对线程维度,threadlocal并未体现出什么优势,为什么还要用threadlocal
作者回复: threadlocal=线程数,局部变量=调用量,差距太大了
2019-05-0712128 - 晓杰不可以,因为ThreadLocal内的变量是线程级别的,而异步编程意味着线程不同,不同线程的变量不可以共享
作者回复: 👍
2019-05-07100 - 承香墨影老师您好,有个问题想请教。 在线程池中使用 ThreadLocal,您给的解决方案是,使用后手动释放。 那这样和使用线程的局部变量有什么区别?每次线程执行的时候都去创建对象并存储在 ThreadLocal 中,用完就释放掉了,下次执行依然需要重新创建,并存入 ThreadLocalMap 中,这样并没有解决局部变量频繁创建对象的问题。
作者回复: 这种用法一般是为了在一个线程里传递全局参数,也叫上下文信息,局部变量不能跨方法,这个用法不是用来解决局部变量重复创建的
2019-05-2266 - QQ怪上面有些同学说多线程是simpledateformat会打印出一样名称的对象,我刚刚也试了下,的确可以复现,但其实是simpledateformat对象的toString()方法搞得鬼,该类是继承object类的tostring方法,如下有个hashcode()方法,但该类重写了hashcode方法,在追溯到hashcode方法,pattern.hashcode(),pattern就是我们的yyyy-MM-dd,这个是一直保持不变的,现在终于真相大白了
作者回复: 感谢回复!!!!
2019-05-07250 - linqw自己写了下对ThreadLocal的源码分析https://juejin.im/post/5ce7e0596fb9a07ee742ba79,感兴趣的可以看下哦,老师也帮忙看下哦
作者回复: 有心👍
2019-05-25215 - So一个ThreadLocal只能保存一个变量,那如果有多个变量要保存,是不是要建多个ThreadLocal?
作者回复: 是的
2019-09-11313 - 晓杰请问一下老师,我刚刚对simpledateformat加threadlocal,但是不同线程得到的simpledateformat对象是一样的,代码如下: public class Tool { public static void main(String[] args) throws Exception{ System.out.println(SafeDateFormat.get()); System.out.println(Thread.currentThread().getName()); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); System.out.println(SafeDateFormat.get()); } }).start(); } static class SafeDateFormat{ static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(()->new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); static SimpleDateFormat get(){ return sdf.get(); } } } 请问存在什么问题
作者回复: 有同学已经找到原因了,是tostring的锅
2019-05-077 - 天天向善有个疑问请教,线程多路复用,使用thread local有什么注意的,会不会不同请求获取内容相同,造成后续业务错误
作者回复: 很有这种可能,所以不能用它存状态数据
2019-05-0825 - 盐多必失某山东省主席…… 宝令小哥哥这加密算法做得太好了,^_^
作者回复: 大智若愚,谁说的清呢,一起鄙视那些不加密的吧😄
2019-06-094 - xinglichea老师, 文中提到解决内存泄露的方法是显示调用remove()方法,但貌似ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value,即:在ThreadLocalMap中的setEntry()、getEntry(),如果遇到key == null的情况,会对value设置为null。 那么是不是可以说明,如果线程在后续操作中会继续调用set()、get()的话,就不需要显示调用remove()了。
作者回复: 工程上建议不要做假设,万一出了问题不好查
2019-08-2623