threadlocal存连接对象的目的_java ThreadLocal(应用场景及使用方式及原理)

通过上面的分析。我们能够认识到ThreadLocal事实上是与线程绑定的一个变量,如此就会出现一个问题:假设没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将会与线程共存。

因此,ThreadLocal的一个非常大的“坑”就是当使用不当时,导致使用者不知道它的作用域范围。

大家可能觉得线程结束后ThreadLocal应该就回收了。假设线程真的注销了确实是这种,可是事实有可能并不是如此。比如在线程池中对线程管理都是採用线程复用的方法(Web容器通常也会採用线程池)。在线程池中线程非常难结束甚至于永远不会结束。这将意味着线程持续的时间将不可预測,甚至与JVM的生命周期一致。

那么对应的ThreadLocal变量的生命周期也将不可预測。

或许系统中定义少量几个ThreadLocal变量也无所谓。由于每次set数据时是用ThreadLocal本身作为Key的,同样的Key肯定会替换原来的数据。原来的数据就能够被释放了,理论上不会导致什么问题。但世事无绝对,假设ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做操作,那么内部的集合类和复杂对象所占用的空间可能会開始膨胀。

抛开代码本身的问题。举一个极端的样例。假设不想定义太多的ThreadLocal变量,就用一个HashMap来存放,这貌似没什么问题。由于ThreadLocal在程序的不论什么一个地方都能够用得到,在某些设计不当的代码中非常难知道这个HashMap写入的源头,在代码中为了保险起见。一般会先检查这个HashMap是否存在,若不存在,则创建一个HashMap写进去。若存在,通常也不会替换掉。由于代码编写者一般会“害怕”由于这样的替换会丢掉一些来自“其它地方写入HashMap的数据”。从而导致很多不可预见的问题。

在这种情况下。HashMap第一次放入ThreadLocal中或许就一直不会被释放,而这个HashMap中可能開始存放很多Key-Value信息,假设业务上存放的Key值在不断变化(比如,将业务的ID作为Key),那么这个HashMap就開始不断变长,并且非常可能在每一个线程中都有一个这种HashMap,逐渐地形成了间接的内存泄漏。以前有非常多人吃过这个亏,并且吃亏的时候发现这种代码可能不是在自己的业务系统中。而是出如今某些二方包、三方包中(开源并不保证没有问题)。

要处理这样的问题非常复杂,只是首先要保证自己编写的代码是没问题的。要保证没问题不是说我们不去用ThreadLocal。甚至不去学习它。由于它肯定有其应用价值。在使用时要明确ThreadLocal最难以捉摸的是“不知道哪里是源头”(一般是代码设计不当导致的),仅仅有知道了源头才干控制结束的部分。或者说我们从设计的角度要让ThreadLocal的set、remove有始有终,通常在外部调用的代码中使用finally来remove数据,仅仅要我们细致思考和抽象是能够达到这个目的的。有些是二方包、三方包的问题,对于这些问题我们须要学会的是找到问题的根源后解决,关于二方包、三方包的执行跟踪,可參看第3.7.9节介绍的BTrace工具。

补充:在不论什么异步程序中(包含异步I/O、非堵塞I/O),ThreadLocal的參数传递是不靠谱的,由于线程将请求发送后。就不再等待远程返回结果继续向下运行了,真正的返回结果得到后,处理的线程可能是还有一个。

#####################################个人总结 ####################################

Thread.java源代码中:

ThreadLocal.ThreadLocalMap threadLocals = null;即:每一个Thread对象都有一个ThreadLocal.ThreadLocalMap成员变量,ThreadLocal.ThreadLocalMap是一个ThreadLocal类的静态内部类(例如以下所看到的),所以Thread类能够进行引用.

static class ThreadLocalMap {所以每一个线程都会有一个ThreadLocal.ThreadLocalMap对象的引用

当在ThreadLocal中进行设值的时候:

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

ThreadLocalMap getMap(Thread t) {

return t.threadLocals;

}

首先获取当前线程的引用,然后获取当前线程的ThreadLocal.ThreadLocalMap对象(t.threadLocals变量就是ThreadLocal.ThreadLocalMap的变量),假设该对象为空就创建一个,例如以下所看到的:

void createMap(Thread t, T firstValue) {

t.threadLocals = new ThreadLocalMap(this, firstValue);

}这个this变量就是ThreadLocal的引用,对于同一个ThreadLocal对象每一个线程都是同样的,可是每一个线程各自有一个ThreadLocal.ThreadLocalMap对象保存着各自ThreadLocal引用为key的值,所以互不影响,并且:假设你新建一个ThreadLocal的对象,这个对象还是保存在每一个线程同一个ThreadLocal.ThreadLocalMap对象之中,由于一个线程仅仅有一个ThreadLocal.ThreadLocalMap对象,这个对象是在第一个ThreadLocal第一次设值的时候进行创建,如上所述的createMap方法.

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);

}

总结:

API说明:

ThreadLocal(),T get(),protected T initialValue(),void remove(),void set(T value)

典型实例:

1.Hiberante的Session 工具类HibernateUtil

2.通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。

ThreadLocal使用的一般步骤:

1、在多线程的类(如ThreadDemo类)中。创建一个ThreadLocal对象threadXxx,用来保存线程间须要隔离处理的对象xxx。

2、在ThreadDemo类中。创建一个获取要隔离訪问的数据的方法getXxx(),在方法中推断,若ThreadLocal对象为null时候,应该new()一个隔离訪问类型的对象,并强制转换为要应用的类型。

3、在ThreadDemo类的run()方法中。通过getXxx()方法获取要操作的数据。这样能够保证每一个线程相应一个数据对象,在不论什么时刻都操作的是这个对象。

与Synchonized的对照:

ThreadLocal和Synchonized都用于解决多线程并发訪问。可是ThreadLocal与synchronized有本质的差别。synchronized是利用锁的机制,使变量或代码块在某一时该仅仅能被一个线程訪问。而ThreadLocal为每个线程都提供了变量的副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时可以获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

一句话理解ThreadLocal:向ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就仅仅属于这个线程了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2020/5月/15好上传最新版 JavaGuide 目前已经 70k+ Star ,目前已经是所有 Java 类别项目中 Star 数量第二的开源项目了。Star虽然很多,但是价值远远比不上 Dubbo 这些开源项目,希望以后可以多出现一些这样的国产开源项目。国产开源项目!加油!奥利给! 随着越来越多的人参与完善这个项目,这个专注 “Java知识总结+面试指南 ” 项目的知识体系和内容的不断完善。JavaGuide 目前包括下面这两部分内容: Java 核心知识总结; 面试方向:面试题、面试经验、备战面试系列文章以及面试真实体验系列文章 内容的庞大让JavaGuide 显的有一点臃肿。所以,我决定将专门为 Java 面试所写的文章以及来自读者投稿的文章整理成 《JavaGuide面试突击版》 系列,同时也为了更加方便大家阅读查阅。起这个名字也犹豫了很久,大家如果有更好的名字的话也可以向我建议。暂时的定位是将其作为 PDF 电子书,并不会像 JavaGuide 提供在线阅读版本。我之前也免费分享过PDF 版本的《Java面试突击》,期间一共更新了 3 个版本,但是由于后面难以同步和订正所以就没有再更新。《JavaGuide面试突击版》 pdf 版由于我工作流程的转变可以有效避免这个问题。 另外,这段时间,向我提这个建议的读者也不是一个两个,我自己当然也有这个感觉。只是自己一直没有抽出时间去做罢了!毕竟这算是一个比较耗费时间的工程。加油!奥利给! 这件事情具体耗费时间的地方是内容的排版优化(为了方便导出PDF生成目录),导出 PDF 我是通过 Typora 来做的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值