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

新疆生产建设兵团一师网站seo顾问推推蛙

新疆生产建设兵团一师网站,seo顾问推推蛙,嘉兴网站设计999 999,网址如何注册前言 ThreadLocal想必都不陌生,当多线程访问同一个共享变量时,就容易出现并发问题,为了保证线程安全,我们需要对共享变量进行同步加锁,但这又带来了性能消耗以及使用者的负担,那么有没有可能当我们创建一个…

前言

ThreadLocal想必都不陌生,当多线程访问同一个共享变量时,就容易出现并发问题,为了保证线程安全,我们需要对共享变量进行同步加锁,但这又带来了性能消耗以及使用者的负担,那么有没有可能当我们创建一个共享变量时,每个线程对其访问的时候访问的都是自己线程的变量呢?没错那就是ThreadLocal。

ThreadLocal使用

举个简单例子:
比如实现一些数据运算的操作,过程中可能需要借助一个临时表去处理数据,临时表有一列存的每一次的执行ID,执行完成根据此次的执行ID进行删除临时表数据。可以使用一个ThreadLocal来存储当前线程的执行ID。

public class DataSyncServiceImpl {private final Logger logger = LoggerFactory.getLogger(DataSyncServiceImpl.class);private static final ThreadLocal<String> execLocalId = ThreadLocal.withInitial(()->new String());@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 借助临时表进行数据运算操作* 临时表字段(id,execution_id)*/public void calculateData(String key){try {execLocalId.set(UUID.randomUUID().toString());calculate();check();System.out.println("同步数据...");}finally {destory();}}private void calculate(){try {System.out.println("数据运算");String execId = execLocalId.get();//...Thread.sleep(1000L);} catch (InterruptedException e) {logger.error("执行异常!",e);}}private void check(){try {System.out.println("数据运算");String execId = execLocalId.get();//...Thread.sleep(1000L);} catch (InterruptedException e) {logger.error("执行异常!",e);}}private void destory(){//根据execution_id删除临时表数据StringBuffer sql = new StringBuffer();sql.append("delete from temp_table where execution_id = ?");jdbcTemplate.update(sql.toString(),execLocalId.get());execLocalId.remove();}
}

这样的话保证了每一个请求线程都有自己的执行ID,清除数据时互不影响。

ThreadLocal实现原理

进入Thread类,可以看到这样两个变量,threadLocals和inheritableThreadLocals,他们都是ThreadLocalMap类型,而ThreadLocalMap是一个类似Map的结构。默认情况下两个变量都为null,当前线程调用set或者get时才会创建。也就是说ThreadLocal变量其实是存在调用线程的内存空间中。每个Thread线程都保存了一个共享变量的副本。

1、threadLocals:当前线程的ThreadLocal变量
2、inheritableThreadLocals:解决子线程不能访问父线程中的ThreadLocal变量

ThreadLocalMap

ThreadLocalMap是一个key为ThreadLocal本身,值为存入的value,对于不同的线程,每次获取副本时,别的线程不能获取到当前线程的副本值,形成了隔离。

Thread和ThreadLocal的关系

在这里插入图片描述

Set方法源码分析

    * 设置当前线程对应的ThreadLocal的值* @param value 将要保存在当前线程对应的ThreadLocal的值*/public void set(T value) {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 判断map是否存在if (map != null)// 存在则调用map.set设置此实体entry,this这里指调用此方法的ThreadLocal对象map.set(this, value);else// 1)当前线程Thread 不存在ThreadLocalMap对象// 2)则调用createMap进行ThreadLocalMap对象的初始化// 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中createMap(t, value);}/*** 获取当前线程Thread对应维护的ThreadLocalMap * * @param  t the current thread 当前线程* @return the map 对应维护的ThreadLocalMap */ThreadLocalMap getMap(Thread t) {return t.threadLocals;}/***创建当前线程Thread对应维护的ThreadLocalMap * @param t 当前线程* @param firstValue 存放到map中第一个entry的值*/void createMap(Thread t, T firstValue) {//这里的this是调用此方法的threadLocalt.threadLocals = new ThreadLocalMap(this, firstValue);}

执行步骤:

获取当前线程,根据当前线程获取到ThreadlocalMap,即threadLocals;
如果获取到的Map不为空,则设置value,key为调用此方法的ThreadLocal引用;
如果Map为空,则先调用createMap创建,再设置value。

Get方法源码分析

     * 返回当前线程中保存ThreadLocal的值* 如果当前线程没有此ThreadLocal变量,* 则它会通过调用{@link #initialValue} 方法进行初始化值* @return 返回当前线程对应此ThreadLocal的值*/public T get() {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 如果此map存在if (map != null) {// 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体eThreadLocalMap.Entry e = map.getEntry(this);// 对e进行判空 if (e != null) {@SuppressWarnings("unchecked")// 获取存储实体 e 对应的 value值,即为我们想要的当前线程对应此ThreadLocal的值T result = (T)e.value;return result;}}/*初始化 : 有两种情况有执行当前代码第一种情况: map不存在,表示此线程没有维护的ThreadLocalMap对象第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry*/return setInitialValue();}/*** 初始化* @return the initial value 初始化后的值*/private T setInitialValue() {// 调用initialValue获取初始化的值// 此方法可以被子类重写, 如果不重写默认返回nullT value = initialValue();// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 判断map是否存在if (map != null)// 存在则调用map.set设置此实体entrymap.set(this, value);else// 1)当前线程Thread 不存在ThreadLocalMap对象// 2)则调用createMap进行ThreadLocalMap对象的初始化// 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中createMap(t, value);// 返回设置的值valuereturn value;}

执行步骤:

1、获取当前线程,获取此线程对象中维护的ThreadLocalMap对象;
2、如果Map不为空,则通过当前调用的ThreadLocal对象获取Entry;
3、判断Entry不为空,则直接返回value;
4、Map或Entry为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为Key和Value创建一个新的Map。

内存泄漏

在这里插入图片描述

从ThreadLocal整体设计上我们可以看到,key持有ThreadLocal的弱引用,GC的时候会被回收,即Entry的key为null。但是当我们没有手动删除这个Entry或者线程一直运行的前提下,存在有强引用链 threadRef->currentThread->threadLocalMap->entry -> value ,value不会被回收,导致内存泄漏。

出现内存泄漏的情况:

1、没有手动删除对应的Entry节点信息,value一直存在。
2、ThreadLocal 对象使用完后,对应线程仍然在运行。

避免内存泄露:

1、使用完ThreadLocal,调用其remove方法删除对应的Entry。
2、对于第二种情况,因为使用了弱引用,当ThreadLocal 使用完后,key的引用就会为null,而在调用ThreadLocal 中的get()/set()方法时,当判断key为null时会将value置为null,这就就会在jvm下次GC时将对应的Entry对象回收,从而避免内存泄漏问题的出现。
在这里插入图片描述

总结

本文主要讲解了ThreadLocal的作用及基本用法,以及ThreadLocal的实现原理和基础方法,注意事项。最后,用ThreadLocal一定要记得用完remove!

http://www.zhongyajixie.com/news/60599.html

相关文章:

  • 项目管理系统软件win7优化教程
  • 福建省住房城乡建设部网站营销策划公司名称
  • 黄网站红烧豆腐怎么做seo超级外链发布
  • 广州荔湾做网站公论坛排名
  • 做个门户网站多少钱网站如何提交百度收录
  • 购物网站毕业论文seo体系
  • 想开民宿自己怎么做介绍的网站爱站网长尾关键词挖掘查询工具
  • php 购物网站开发网站打开
  • wordpress slider代码太原百度seo
  • 临沂网站建设有哪些西安今日头条新闻消息
  • easyweb wordpress网站怎么优化关键词
  • 地下城做心悦任务的网站百姓网推广电话
  • 重庆做网站公司哪家比较好一个新品牌怎样营销推广
  • mvc网站开发 案例视频西安百度关键词优化
  • 企业网站设计建设服务器百度注册网站
  • 独立建站是什么意思网站信息查询
  • 日本樱花云服务器网站打广告
  • 简单扁平化风格后台网站模板做网络推广的团队
  • 贵阳网站制作工具网络广告的类型有哪些
  • 广州市场监督管理局官网百度关键词优化送网站
  • 安庆专业做淘宝网站爱站网是什么
  • wordpress海外建站优化好搜移动端关键词快速排名
  • 万维网站建设品牌推广策略
  • 网站建设的论坛网络运营培训班多少钱
  • asp.net做电商网站页面佛山网页搜索排名提升
  • 园区 网站建设方案上海网优化seo公司
  • 武汉十大营销策划公司西安seo关键词推广
  • 做粘土的网站网站优化的方式有哪些
  • 广告传媒公司网站怎么做百度广告电话号码
  • 网站建设的好处和目的长沙网络公司营销推广