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

建设工程公司网站瓯海住房与城乡建设局网站

建设工程公司网站,瓯海住房与城乡建设局网站,网站asp模板,明天上海封控16个区1. ThreadLocal 是什么 JDK 对ThreadLocal的描述为#xff1a; 此类提供线程局部变量。这些变量与普通变量的不同之处在于#xff0c;每个访问一个变量的线程#xff08;通过其get或set方法#xff09;都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是类中的私有…1. ThreadLocal 是什么 JDK 对ThreadLocal的描述为 此类提供线程局部变量。这些变量与普通变量的不同之处在于每个访问一个变量的线程通过其get或set方法都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段这些字段希望将状态与线程例如用户ID或事务ID相关联。 说白了ThreadLocal就是用来存放线程自身相关数据的一个容器这个容器叫做ThreadLocalMap它是ThreadLocal的一个静态内部类同时作为Thread类的一个成员变量。ThreadLocal在使用时先拿到当前线程的成员变量ThreadLocalMap以当前的ThreadLocal对象作为key变量作为value存入ThreadLocalMap。 然后每个线程取变量都是从线程各自的ThreadLocalMap中取值自然是线程安全的了。因为变量只在自己线程的生命周期内起作用所以说ThreadLocal提供线程局部变量或者叫线程本地变量。 ThreadLocal 的特点有3个 线程并发在多线程并发的场景下使用。数据传递通过 ThreadLocal 在同一个线程中不同组件中传递公共变量。线程隔离不同线程之间互不干扰这种变量在线程的生命周期内起作用。 2. ThreadLocal 怎么用 ThreadLocal 的常用方法有 public ThreadLocal()通过构造器创建对象。一般是静态的。S ThreadLocalS withInitial(Supplier? extends S supplier)初始化一个 ThreadLcoal。void set(T value)设置当前线程绑定的局部变量。T get()获取当前线程绑定的局部变量。void remove()删除当前线程绑定的局部变量。 2.1 使用入门 2.1.1 原始版本 现在模拟一个需求一个线程在业务开始时初始化一个用户 id类似在一次web请求中上下文中初始化一下用户信息业务结束时获取这个用户 id比如用来打印日志或者作为一个公共变量运用到业务编码中存在多个这样的线程。 public class ThreadLocalTest {private String userId;private String getUserId() {return userId;}private void setUserId(String userId) {this.userId userId;}public static void main(String[] args) {ThreadLocalTest test new ThreadLocalTest();for (int i 1; i 6; i) {Thread thread new Thread(() - {// 当前线程初始化userIdtest.setUserId(Thread.currentThread().getName() 的userId);// 执行其他业务代码System.out.println(执行业务代码);// 当前线程获取userIdSystem.out.println(Thread.currentThread().getName() -- test.getUserId());});thread.setName(线程 i);thread.start();}} }一种可能的结果 执行业务代码 线程2--线程1的userId 执行业务代码 线程1--线程3的userId 执行业务代码 线程3--线程3的userId 执行业务代码 线程4--线程4的userId由于线程调度的不确定性可能线程1运行到一半切换到了线程2于是线程2获取到的 userId 是线程1设置的。也就是说每个线程之间的变量不是隔离的造成数据错误。 2.1.2 ThreadLocal 版本 每个线程中的变量都存放到自己的线程当中所以这些变量叫做线程局部变量很形象。 public class ThreadLocalTest {private static ThreadLocalString context new ThreadLocal();private String getUserId() {return context.get();}private void setUserId(String userId) {context.set(userId);}public static void main(String[] args) {ThreadLocalTest test new ThreadLocalTest();for (int i 1; i 5; i) {Thread thread new Thread(() - {test.setUserId(Thread.currentThread().getName() 的userId);System.out.println(执行业务代码);System.out.println(Thread.currentThread().getName() -- test.getUserId());context.remove(); // 使用完清理线程局部变量});thread.setName(线程 i);thread.start();}} }这样每个线程就互不干扰不会取错变量值。一种可能的结果如下 执行业务代码 线程1--线程1的userId 执行业务代码 线程4--线程4的userId 执行业务代码 线程2--线程2的userId 执行业务代码 线程3--线程3的userId2.1.3 synchronized 版本 如果只看结果的正确性用 synchronized 给业务代码块加锁也是可以完成的。如下 Thread thread new Thread(() - {synchronized (ThreadLocalTest.class) {test.setUserId(Thread.currentThread().getName() 的userId);System.out.println(执行业务代码);System.out.println(Thread.currentThread().getName() - test.getUserId());} });这样完全可以实现需求但是 synchronized 的问题是什么呢我们总说谁谁谁是线程安全的类因为它有 synchronized 修饰。就是因为 synchronized 让多线程变成了单线程它一次只允许一个线程执行它能不安全吗但它带来的代价是性能的下降它不能并发执行而 ThreadLocal 可以并发执行。 2.1.4 ThreadLocal 和 synchronized 对比 综上synchronized 和 ThreadLocal 两个处理问题的角度和场景是不同的。 synchronized 的侧重点在于保证操作的原子性保证并发场景下共享变量的数据一致性。ThreadLocal 强调线程隔离性不同的线程互不干扰保证并发场景下数据传递的正确性。在web请求上下文中较为常见。 3. ThreadLocal 原理 3.1 代码结构 ThreadLocal 的原理要从它的set(T value)、get()方法的源码入手。在 set 值的时候首先会获取当前线程一个的成员变量ThreadLocalMapThreadLocalMap的 key 是当前ThreadLocal对象value 是要存入的值。这个 key 和 value 会存到哪里呢ThreadLocalMap还有个内部类Entry这个Entry继承了WeakReferencekey 赋值给弱引用也就是当前的ThreadLocal对象value 则赋值给Entry的成员变量value。ThreadLocalMap也是一个哈希表所谓哈希表也叫散列表它基于数组通过某种哈希算法计算出一系列关键字对应的散列值然后以这些散列值作为数组索引将数据存放到对应位置达到快速查找的目的它内部维护一个Entry数组来存储键值对。存数据的时候也是通过哈希函数计算ThreadLocal 对象对应的数组下标然后放入Entry数组中。 3.2 内存泄漏问题 ThreadLocal 会发生内存泄漏吗我们结合代码慢慢分析。 在 2.1.1 节中有这样的代码 public class ThreadLocalTest {private static ThreadLocalString threadLocal new ThreadLocal();private void setUserId(String userId) {threadLocal.set(userId);}// ... }首先我们new了一个 ThreadLocal 对象这里存在一个强引用threadLocal引用变量指向 ThreadLocal 对象。其次当其他线程执行setUserId方法时ThreadLocal 的set方法最终是把数据存到了ThreadLocalMap中的Entry看源码我们会发现存数据最终是调用Entry的构造器Entry(ThreadLocal? k, Object v)完成的而k这个参数是传入的this对象说明什么我们使用 ThreadLocal 对象调用set那this肯定是当前new出来的 ThreadLocal 对象再次说明我们new出来的 ThreadLocal 对象有两个引用指向它 threadLocal变量的强引用。在Entry中的弱引用。 此时再看一张图这张图被广泛引用感谢原图作者 堆内存里面有个 ThreadLocal 对象它被两个箭头指着实线代表强引用虚线代表弱引用。有两个引用链一个是我们手动创建的threadLocal的引用变量指向的即图中的 ThreadLcoal Ref 对应示例代码中的threadLocal变量一个是由于调用了 ThreadLocal 的set或get方法初始化了当前线程的ThreadLocalMap再初始化 Map 中的Entry对象再初始化Entry对象中的 key 和 value形成一个由当前线程对象到它内部变量的引用链即上图中的 Current Thread Ref它对应set方法源码中的这一行Thread t Thread.currentThread();中的变量t。 那问题来了如果这个手动创建的 ThreadLocal 对象 的『引用变量』被回收了那 ThreadLocal 对象 是不是只剩下Entry中 key 的弱引用了而弱引用的对象会随时被 GC 回收即Entry中的 key 会在 GC 后变为null了。我们知道ThreadLocalMap的 key 是当前的 ThreadLocal 对象那 key 为null了之后就无法获取到Entry也取不到 value 的值了。在Entry对象没有被主动删除或者当前线程没有终结的情况下该Entry一直处在一个由当前线程指向的强引用链中。由于这个Entry获取不到就一直占用着内存又因为强引用不能被 GC 回收所以这个Entry就发生了内存泄漏。如果这个线程是一个普通线程在线程终止的时候整个线程对象被回收了那内存泄漏的时间比较短如果该线程一直不终止比如线程池中的核心线程那内存泄露问题就一直存在了。 注意上面说的“如果这个手动创建的 ThreadLocal 对象 的『引用变量』被回收了”应该会有人疑惑这种情况什么时候会发生呢第一种情况手动把这个引用变量置为null虽然概率小但也不是没可能第二种情况引用变量是存在栈内存中当方法执行完就会立即回收栈内存中的引用变量即堆内存中的实际对象失去引用指针了。这种情况就比如 ThreadLocal 是在方法中创建的局部变量。 3.3 为什么使用弱引用 Entry的 key 使用弱引用有内存泄漏风险那为什么 JDK 还是使用弱引用而不是强引用 我们分两种情况讨论 key 使用强引用ThreadLocal 的引用变量被回收了这句话意味着什么呢引用变量被回收了意味着代码中不再使用 ThreadLocal 这个对象了因为要使用 ThreadLocal 这个对象我们需要用它的引用变量取调set、get方法现在引用变量没了我们就用不了 ThreadLocal 这个对象了。但问题是ThreadLocalMap还持有ThreadLocal对象的强引用当前线程到Entry的强引用链依然存在。注意前面提到了ThreadLocal 对象已经不再使用了也就是说Entry就获取不到了。如果Entry没有手动删除或者线程没有结束这个没用的Entry也会一直保留依然发生内存泄漏要明白内存泄漏是对象没用了还存在内存中不被回收的情况。key 使用弱引用前面已经分析过了ThreadLocal 的引用变量被回收了ThreadLocal对象也被回收导致Entry的 key 变成null在没有手动删除Entry或线程不结束时依然发生内存泄漏。 归根结底由于ThreadLocalMap的生命周期跟Thread一样长在 ThreadLocal 的引用变量消失后如果线程不结束原来的Entry就不会回收这就是内存泄漏的本质。虽然 ThreadLocal 在每次读写数据的时候都会将key为null的Entry清空但是既然 ThreadLocal 的引用变量都消失了我们也没机会再set或get了。 那为什么使用弱引用我也不知道我还没想明白如果正在阅读的你知道请你告诉我下谢谢。虽然ThreadLocalMap的注释中解释了 To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys. 为了帮助处理非常大和长期的使用哈希表条目使用WeakReferences作为键。 我觉得没必要取纠结这个问题只要规范的使用 ThreadLocal几乎不会发生内存泄漏。 3.4 如何防止内存泄漏 把 ThreadLocal 对象申明为类变量。类变量的生命周期跟 JVM 是同步的这样 ThreadLocal 的强引用就一直存在不会被 GC 回收Entry的key就不会发生null的情况了。使用完 ThreadLocal 后用remove()方法清空当前ThreadLocal 对应的数据对应的Entry就不占内存了。 第一种情况虽热能避免Entry的key为null的情况但是如果后续线程不再访问这个 key且线程不结束时这个 key 对应的数据也会一直存在内存中容易造成内存溢出的问题。所以最好的办法就是在 ThreadLocal 使用完之后使用remove()方法清除数据。 4. ThreadLocal 如何存多个变量 上面的示例代码中ThreadLocal 只存了一个变量实际情况不可能只存一个吧多个变量如何存如何取 要知道 ThreadLocal 使用set方法存数据时key 用的this对象就是当前正在使用的 ThreadLocal 对象说明一个 ThreadLocal 对象在一个线程中只能存一个线程本地变量。多个线程虽然都是用的是一个 key但是不同的线程用的是不同的ThreadLocalMap。 第一种方案是多 new 几个 ThreadLocal 对象每个 ThreadLocal 对象对应一个业务变量。 第二种方法就是在给 ThreadLocal 初始化一个HashMap这是最常规的做法。比如下面 public class ThreadLocalTest {private static final ThreadLocalMapString, Object context ThreadLocal.withInitial(HashMap::new);private String getUserId() {return String.valueOf(context.get().get(userId));}private void setUserId(String userId) {context.get().put(userId, userId);}public void setUserName(String userName) {context.get().put(userName, userName);}public String getUserName() {return String.valueOf(context.get().get(userName));}public static void main(String[] args) {ThreadLocalTest test new ThreadLocalTest();for (int i 1; i 5; i) {Thread thread new Thread(() - {String threadName Thread.currentThread().getName();test.setUserId(threadName 的userId);test.setUserName(threadName 的userName);System.out.println(执行业务代码);System.out.println(threadName -- test.getUserId() , test.getUserName());});thread.setName(线程 i);thread.start();}} }一种可能的结果 执行业务代码 线程2--线程2的userId,线程2的userName 执行业务代码 线程4--线程4的userId,线程4的userName 执行业务代码 线程3--线程3的userId,线程3的userName 执行业务代码 线程1--线程1的userId,线程1的userName5. 为什么用 ThreadLocal 5.1 ThreadLocal的使用场景 线程的上下文传递。企业中最常见的是应用到web请求的上下文一个 Http 请求会经过一系列拦截器过滤器最后到达服务层在这个调用链路中会频繁的使用到一些公共数据如用户信息或请求的ID把这些公共数据放到 ThreadLocal 中会在请求的链路中非常方便的使用这些信息。 还有一些框架中会使用 ThreadLocal 来管理数据库连接避免了线程之间的竞争。比如 Mybatis 就是用 ThreadLocal 来存储Sqlsession对象。 5.2 使用 ThreadLocal 的好处 使用 ThreadLocal 的好处是并发场景下减少了同一个线程内多个函数或组件之间传递公共变量的复杂度且提高了使用这些共享变量的安全性。
http://www.hkea.cn/news/14271494/

相关文章:

  • 中国兰州网pc主站网站切图
  • 杭州网站推广找哪家深圳分为哪几个区
  • 娄底网站设计江西机场建设集团网站
  • 桂城网站制作想在网上做开发网站接活儿
  • 网站建设下坡路深圳自助网站建设费用
  • 潍坊模板建站平台网站友情链接 关键词经常改动
  • 二维码网站建设p2p网站建设要点
  • 网站建设费做什么网站运营如何做
  • 石龙做网站购物网站建设基本流程
  • 免费浪漫网页制作网站竞价
  • 安康做网站的公司电话网上做调查网站有哪些
  • 软件定制开发 报价医疗网站建设及优化
  • 厦门网站的建设山东泰安房价
  • 苏州有哪些做网站公司wordpress 编辑 按钮
  • 怎么做应援网站怎么做网站导流生意
  • 为什么要用模板建站网站建设初步策划方案
  • 网站开发必须要搭建环境吗聊城专业建设学校
  • 秦皇岛做网站的公司37网页游戏中心
  • 山东省建设厅网站特种作业网上注册公司核名流程
  • 如何在电影网站中做淘客网络域名
  • 站内推广固原住房和城乡建设厅网站
  • ios网站开发工具有哪些软件工程主要课程
  • 做照明出口的网站做程序网站需要什么代码
  • 印刷网站 源码seo网站内容优化有哪些
  • 广南酒店网站建设seo公司电信上海百首网络
  • 每个网站都有服务器吗淘宝上网站建设是什么
  • ui毕业设计代做网站北京管庄网站建设公司
  • html网页设计网站网站关键词用什么做
  • 用python做音乐网站免费客源软件
  • 沈阳网站建设莫道网络网站代理公司