博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ThreadLocal
阅读量:5348 次
发布时间:2019-06-15

本文共 3225 字,大约阅读时间需要 10 分钟。

ThreadLocal


该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其getset方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的私人静字段,它们希望将状态与某一个线程(例如,用户ID或事务ID)相关联。

其公共方法只有三个:

  • public T get() { } 返回此线程局部变量的当前线程副本中的值。
  • public void set(T value) { } 将此线程局部变量的当前线程副本中的值设置为指定值。
  • public void remove() { } 移除此线程局部变量当前线程的值。
  • protected T initialValue() { } initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法

ThreadLocal内部还有一个静态内部类ThreadLocalMap,该内部类才是实现线程隔离机制的关键,get()、set()、remove()都是基于该内部类操作。ThreadLocalMap提供了一种用键值对方式存储每一个线程的变量副本的方法,key为当前ThreadLocal对象,value则是对应线程的变量副本。

ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。值得注意的是图中的虚线,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。

ThreadLocal内存泄漏

ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现keynullEntry,就没有办法访问这些keynullEntryvalue,如果当前线程再迟迟不结束的话,这些keynullEntryvalue就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。

其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocalget(),set(),remove()的时候都会清除线程ThreadLocalMap里所有keynullvalue

注意

ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

ThreadLocal 最佳实践

综合上面的分析,我们可以理解ThreadLocal内存泄漏的前因后果,那么怎么避免内存泄漏呢?

  • 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。

使用案例

package my.syn;        import java.util.concurrent.CountDownLatch;        import static my.syn.MyGame.countDownLatch;        import static my.syn.MyGame.threadLocal;/** * @ClassName: MyGame * @author: Yang.X.P * @date: 2018-09-17 16:24 **/public class MyGame implements Runnable{    static CountDownLatch countDownLatch = new CountDownLatch(4);    static ThreadLocal
threadLocal = new ThreadLocal<>(); @Override public void run() { System.out.println("游戏大厅已加载好,等待所有玩家准备好,即可进入"); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有人到准备好了,游戏开始..."); }}class Aircraft implements Runnable { @Override public void run() { System.out.println("飞机准备好了,等待起飞"); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("玩家到齐,飞机起飞!"); }}class Player implements Runnable { @Override public void run() { threadLocal.set(Thread.currentThread().getName() + "的threadLocal"); System.out.println(threadLocal.get()); threadLocal.remove(); System.out.println(Thread.currentThread().getName() + "准备好了"); countDownLatch.countDown(); } public static void main(String[] args){ Player player = new Player(); MyGame myGame = new MyGame(); Aircraft aircraft = new Aircraft(); Thread airThread = new Thread(aircraft); Thread gameThread = new Thread(myGame); gameThread.start(); airThread.start(); for (int i = 0; i < 4; i++) { Thread playerThread = new Thread(player); playerThread.start(); } }}

参考资料:

转载于:https://www.cnblogs.com/fruitknife/p/9703159.html

你可能感兴趣的文章
关于空想X
查看>>
CF1067C Knights 构造
查看>>
[BZOJ2938] 病毒
查看>>
webstorm修改文件,webpack-dev-server不会自动编译刷新
查看>>
Scikit-learn 库的使用
查看>>
CSS: caption-side 属性
查看>>
python 用数组实现队列
查看>>
认证和授权(Authentication和Authorization)
查看>>
Mac上安装Tomcat
查看>>
CSS3中box-sizing的理解
查看>>
传统企业-全渠道营销解决方案-1
查看>>
Lucene全文检索
查看>>
awk工具-解析1
查看>>
推荐一款可以直接下载浏览器sources资源的Chrome插件
查看>>
CRM product UI里assignment block的显示隐藏逻辑
查看>>
AMH V4.5 – 基于AMH4.2的第三方开发版
查看>>
Web.Config文件配置之配置Session变量的生命周期
查看>>
mysql导入source注意点
查看>>
linux下编译安装nginx
查看>>
ArcScene 高程不同的表面无法叠加
查看>>