当前位置: 首页 > news >正文

封面设计网站网站开通

封面设计网站,网站开通,嘉定区网站建设公司,室内设计师培训班费用使用ThreadLocal可以为每个线程维护一个线程变量,使用场景为线程间隔离,线程内方法共享; 原理: Thread类中有一个实例属性ThreadLocalMap,ThreadLocalMap中存放的是Entry数组,Entry数组是ThreadLocal和Ob…

使用ThreadLocal可以为每个线程维护一个线程变量,使用场景为线程间隔离,线程内方法共享;

原理:

Thread类中有一个实例属性ThreadLocalMap,ThreadLocalMap中存放的是Entry数组,Entry数组是ThreadLocal和Object的键值对;源码如下--

Thread类:

/** ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals;

ThreadLocal->ThreadLocalMap->Entry类:

static class ThreadLocalMap {/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal object).  Note that null keys (i.e. entry.get()* == null) mean that the key is no longer referenced, so the* entry can be expunged from table.  Such entries are referred to* as "stale entries" in the code that follows.*/static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}

ThreadLocal有一个内部静态类ThreadLocalMap, ThreadLocalMap是一个Entry数组,通过map方法来访问数据,而Entry对key(ThreadLocal实例对象)的引用使用了虚引用;这里导致key可能会在JVM判断oom之前将key回收掉,导致内存泄漏-此时key对应的ThreadLocal对象被回收变成null,而value存在Thread reference->thread->threadlocalMap->entry-value的强引用链,导致无法回收;虽然在ThreadLocal的get和set以及remove方法中实现了检查是否存在key为null并清理value的机制,但是无法避免内存短时间内泄露的问题;

看一下ThreadLocal的set()和get()方法--

public void set(T value) {set(Thread.currentThread(), value);}private void set(Thread t, T value) {ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
public T get() {return get(Thread.currentThread());}private T get(Thread t) {//获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {T result = (T) e.value;return result;}}return setInitialValue(t);}private Entry getEntry(ThreadLocal<?> key) {//这里进行map操作和hashmap的原理基本是一样的,hashcode按位与数组长度减一,数组长度默认为16,(2的n次方减一值做按位与相当于取模,保证了散列均衡)int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.refersTo(key))return e;elsereturn getEntryAfterMiss(key, i, e);}

所以,ThreadLocal维护为每一个线程维护线程变量副本的原理在于,每个线程对象会创建自己的THreadLocalMap--这里并非主动创建,而是调用THreadLocal的set或者get方法的时候才创建;而ThreadLocalMap则是一个以ThreadLocal实例对象的弱应用为key,Object对象为value的键值对数组,以便每个线程对象存放多个线程变量,而对这个线程变量的访问是通过ThreadLocal对象来操作的,所以说实现线程变量的根本不在于ThreadLocal,而是ThreadLocalMap,而ThreadLocal对象只是访问该线程的ThreadLocalMap的一个入口,由于get(Thread t)和set(Thread t,T value)都是私有方法,通过其他对象无法访问,所以只能通过ThreadLocal对象提供的重载方法get()和set(Object value)来访问,保证了线程变量的安全性---其他线程无法修改另外一个线程的ThreadLocalMap;所以核心是getMap(Thread t),而这里的线程对象t只能是Thread.currentThread;

另外需要注意的是,对ThreadLocal 保存的线程变量一定要在使用完之后及时remove掉,一个原因是防止内存泄漏,更重要的原因是在线程池场景下,一个请求可能会读取到上一个请求保存的变量内容,从而造成业务逻辑上的BUG,而且这种BUG还是不易排查的;

另外关于内存泄露的问题JDK提供了一些保障,分为启发式清理和探测式清理--

探测式清理--

原理:

  1. 删除当前元素;
  2. 继续往后遍历,
    1. 当key=null的时候,删除value
    2. 当key!=null的时候,将因哈希碰撞后移的元素重新放置----ThreadLocalMap中使用了后移解决哈希碰撞

源码:

private int expungeStaleEntry(int staleSlot) {Entry[] tab = table;int len = tab.length;// 删除当前元素--value置为null,value引用的对象变成了无引用对象,会被回收;,同样的将数组上该位置的Entry对象置空,原来的Entry对象也会被回收掉;tab[staleSlot].value = null;tab[staleSlot] = null;size--;// Rehash until we encounter nullEntry e;int i;//继续遍历for (i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();//key为空的时候继续删除if (k == null) {e.value = null;tab[i] = null;size--;} else {//key 不为空的时候rehash,重新计算该key的下标---这里需要注意--rehash的结果可能和原来的值是一样的,因为不能完全保证是否产生过哈希碰撞int h = k.threadLocalHashCode & (len - 1);if (h != i) {tab[i] = null;// Unlike Knuth 6.4 Algorithm R, we must scan until// null because multiple entries could have been stale.while (tab[h] != null)h = nextIndex(h, len);tab[h] = e;}}}return i;}

启发式清理--

        往后遍历,如果找到key为null的过期元素, 则调用探测式清理--启发式清理可以理解为为探测式清理找到一个清理入口,由探测式清理完成具体的清理工作

private boolean cleanSomeSlots(int i, int n) {boolean removed = false;Entry[] tab = table;int len = tab.length;do {i = nextIndex(i, len);Entry e = tab[i];if (e != null && e.refersTo(null)) {n = len;removed = true;i = expungeStaleEntry(i);}} while ( (n >>>= 1) != 0);return removed;}

另外看一下清理工作在哪里被调用--

  1. set(Thread t,T value)->cleabSomeSlots();
  2. set(Thread t,T value)->replaceStaleEntry()->cleanSomeSlots();
  3. get(t)->getEntry()->getEntryAfterMiss()->expungeStaleEntry();
  4. remove()->expungeStaleEntry();
  5. rehash()->expungeStaleEntries->expungeStaleEntry()

上边的调用关系可以看出来基本上我们对ThreadLocal的get,set,remove操作都会触发清理工作,尽量减小了内存泄漏带来的影响---在未做任何操作之前不会主动触发清理,所以在使用ThreadLocal的时候及时调用remove很重要!!!

http://www.yidumall.com/news/90150.html

相关文章:

  • 网站介绍视频怎么做的抚州seo排名
  • wps的ppt做网站超链接智能搜索引擎
  • 每个网站都有服务器吗seo的概念
  • 宝安公司网站建设比较好的重庆网络seo
  • 做音频的网站计算机编程培训学校哪家好
  • 洛阳哪里做网站培训学校加盟
  • 申通物流的网站建设20个排版漂亮的网页设计
  • 长沙做网站哪家好网站关键词优化排名公司
  • swoole做网站免费外链发布平台在线
  • 武威建设银行网站网站关键词上首页
  • 邹城网站制作广点通
  • vps服务器购买app关键词排名优化
  • 网站开发法律可行性seo优化的方法有哪些
  • 郑州做网站的多不多seo公司哪家好用
  • 重庆seo排名方法win7优化工具
  • 北京好的医疗网站设计域名在线查询
  • 铁岭做网站哪家好怎样推广一个产品
  • 网站开发怎么让别人看到百度首页推广
  • 网站开发流程 百度文库国外独立网站如何建站
  • 政府网站互动平台的建设郑州seo技术顾问
  • 香港免备案虚拟主机电脑优化大师
  • 新乡网络推广外包合肥网站优化
  • 广东 品牌网站建设it培训
  • 网站花瓣飘落的效果怎么做全国新冠疫情最新情况
  • web前端做音乐网站seo推广优化平台
  • 做百度推广是不是得有个网站先网站推广怎么写
  • 网站开发具体工作有那些公司产品怎样网上推广
  • 哪里有网站建设商家江苏网站seo设计
  • 松江郑州阳网站建设怎样才能被百度秒收录
  • 德州做网站最好的公司有哪些网站seo的内容是什么