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

济南单位网站建设个体营业执照网上申请

济南单位网站建设,个体营业执照网上申请,rails 开发的网站开发,怎么样制作自己的网站引言 ThreadLocal在Java多线程编程中扮演着重要的角色#xff0c;它提供了一种线程局部存储机制#xff0c;允许每个线程拥有独立的变量副本#xff0c;从而有效地避免了线程间的数据共享冲突。ThreadLocal的主要用途在于#xff0c;当需要为每个线程维护一个独立的上下文…引言 ThreadLocal在Java多线程编程中扮演着重要的角色它提供了一种线程局部存储机制允许每个线程拥有独立的变量副本从而有效地避免了线程间的数据共享冲突。ThreadLocal的主要用途在于当需要为每个线程维护一个独立的上下文变量时比如每个线程的事务ID、用户登录信息、数据库连接等可以减少对同步机制如synchronized关键字或Lock类的依赖提高系统的执行效率和简化代码逻辑。 但是我们在使用ThreadLocal时经常因为使用不当导致内存泄漏。此时就需要我们去探究一下ThreadLocal在哪些场景下会出现内存泄露哪些场景下不会出现内存泄露出现内存泄露的根本原因又是什么呢如何避免内存泄露 ThreadLocal原理 ThreadLocal的实现基于每个线程内部维护的一个ThreadLocalMap。 public class Thread implements Runnable {/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals null; }ThreadLocalMap是ThreadLocal类的一个静态内部类ThreadLocal本身不能存储数据它在作用上更像一个工具类ThreadLocal类提供了set(T value)、get()等方法来操作ThreadLocalMap存储数据。 public class ThreadLocalT {// ...public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null)map.set(this, value);elsecreateMap(t, value);}public T get() {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {ThreadLocalMap.Entry e map.getEntry(this);if (e ! null) {SuppressWarnings(unchecked)T result (T)e.value;return result;}}return setInitialValue();}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}// ... }而ThreadLocalMap内部维护了一个Entry数据用来存储数据Entry继承了WeakReference所以Entry的key是一个弱引用可以被GC回收。Entry数组中的每一个元素都是一个Entry对象。每个Entry对象中存储着一个ThreadLocal对象与其对应的value值。 static class ThreadLocalMap {static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}} }关于弱引用的知识点请参考美团一面说一说Java中的四种引用类型 而Entry数组中Entry对象的下标位置是通过ThreadLocal的threadLocalHashCode计算出来的。 private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable parentMap.table;int len parentTable.length;setThreshold(len);table new Entry[len];for (Entry e : parentTable) {if (e ! null) {SuppressWarnings(unchecked)ThreadLocalObject key (ThreadLocalObject) e.get();if (key ! null) {Object value key.childValue(e.value);Entry c new Entry(key, value);// 通过key的threadLocalHashCode计算下标这个key就是ThreadLocall对象int h key.threadLocalHashCode (len - 1);while (table[h] ! null)h nextIndex(h, len);table[h] c;size;}}} } 而从Entry数组中获取对应key即ThreadLocal对应的value值时也是通过key的threadLocalHashCode计算下标从而可以快速的返回对应的Entry对象。 private Entry getEntry(ThreadLocal? key) { // 通过key的threadLocalHashCode计算下标这个key就是ThreadLocall对象int i key.threadLocalHashCode (table.length - 1);Entry e table[i];if (e ! null e.get() key)return e;elsereturn getEntryAfterMiss(key, i, e); }在Thread中可以存储多个ThreadLocal对象。Thread、ThreadLocal、ThreadLocalMap以及Entry数组的关系如下图 ThreadLocal在哪些场景下不会出现内存泄露 当一个对象失去所有强引用或者它仅被弱引用、软引用、虚引用关联时垃圾收集器GC通常都能识别并回收这些对象从而避免内存泄漏的发生。当我们在手动创建线程时若将变量存储到ThreadLocal中那么在Thread线程正常运行的过程中它会维持对内部ThreadLocalMap实例的引用。只要该Thread线程持续执行任务这种引用关系将持续存在确保ThreadLocalMap实例及其中存储的变量不会因无引用而被GC回收。 当线程执行完任务并正常退出后线程与内部ThreadLocalMap实例之间的强引用关系随之断开这意味着线程不再持有ThreadLocalMap的引用。在这种情况下失去强引用的ThreadLocalMap对象将符合垃圾收集器GC的回收条件进而被自动回收。与此同时鉴于ThreadLocalMap内部的键ThreadLocal对象是弱引用一旦ThreadLocalMap被回收若此时没有其他强引用指向这些ThreadLocal对象它们也将被GC一并回收。因此在线程结束其生命周期后与之相关的ThreadLocalMap及其包含的ThreadLocal对象理论上都能够被正确清理避免了内存泄漏问题。 实际应用中还需关注ThreadLocalMap中存储的值非键是否为强引用类型因为即便键ThreadLocal对象被回收如果值是强引用且没有其他途径释放仍可能导致内存泄漏。 ThreadLocal在哪些场景下会出现内存泄露 在实际项目开发中如果为每个任务都手动创建线程这是一件很耗费资源的方式并且在阿里巴巴的开发规范中也提到不推荐使用手动创建线程推荐使用线程池来执行相对应的任务。那么当我们使用线程池时线程池中的线程跟ThrealLocalMap的引用关系如下 在使用线程池处理任务时每一个线程都会关联一个独立的ThreadLocalMap对象用于存储线程本地变量。由于线程池中的核心线程在完成任务后不会被销毁而是保持活动状态等待接收新的任务这意味着核心线程与其内部持有的ThreadLocalMap对象之间始终保持着强引用关系。因此只要核心线程存活其所对应的ThreadLocal对象和ThreadLocalMap不会被垃圾收集器GC自动回收此时就会存在内存泄露的风险。 关于Java中的线程池参数以及原理请参考Java线程池最全讲解 出现内存泄露的根本原因 由上述ThreadLocalMap的结构图以及ThreadLocalMap的源码中我们知道ThreadLocalMap中包含一个Entry数组而Entry数组中的每一个元素就是Entry对象Entry对象中存储的Key就是ThreadLocal对象而value就是要存储的数据。其中Entry对象中的Key属于弱引用。 static class ThreadLocalMap {static class Entry extends WeakReferenceThreadLocal? {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal? k, Object v) {super(k);value v;}} }而对于弱引用WeakReference在引用的对象使用完毕之后即使内存足够GC也会对其进行回收。 关于弱引用的知识点请参考美团一面说一说Java中的四种引用类型 当Entry对象中的Key被GC自动回收后对应的ThreadLocal被GC回收掉了变成了null但是ThreadLocal对应的value值依然被Entry引用不能被GC自动回收。这样就造成了内存泄漏的风险。 在线程池环境下使用ThreadLocal存储数据时内存泄露的风险主要源自于线程生命周期管理及ThreadLocalMap内部结构的设计。由于线程池中的核心线程在完成任务后会复用每个线程都会维持对各自关联的ThreadLocalMap对象的强引用这确保了只要线程持续存在其对应的ThreadLocalMap就无法被垃圾收集器GC自动回收。 进一步分析ThreadLocalMap内部采用一个Entry数组来保存键值对其中每个条目的Key是当前线程中对应ThreadLocal实例的弱引用这意味着当外部不再持有该ThreadLocal实例的强引用时Key部分能够被GC正常回收。然而关键在于Entry的Value部分它直接或间接地持有着强引用的对象即使Key因为弱引用特性被回收但Value所引用的数据却不会随之释放除非明确移除或者整个ThreadLocalMap随着线程结束而失效。 所以在线程池中如果未正确清理不再使用的ThreadLocal变量其所持有的强引用数据将在多个任务执行过程中逐渐积累并驻留在线程的ThreadLocalMap中从而导致潜在的内存泄露风险。 ThreadLocal如何避免内存泄漏 经过上述ThreadLocal原理以及发生内存泄漏的分析我们知道防止内存泄漏我们一定要在完成线程内的任务后调用ThreadLocal的remove()方法来清除当前线程中ThreadLocal所对应的值。其remove方法源码如下 public void remove() {ThreadLocalMap m getMap(Thread.currentThread());if (m ! null) {m.remove(this);}}在remove()方法中首先根据当前线程获取ThreadLocalMap类型的对象如果不为空则直接调用该对象的有参remove()方法移除value的值。ThreadLocalMap的remove方法源码如下 private void remove(ThreadLocal? key) {Entry[] tab table;int len tab.length;int i key.threadLocalHashCode (len-1);for (Entry e tab[i];e ! null;e tab[i nextIndex(i, len)]) {if (e.get() key) {e.clear();expungeStaleEntry(i);return;}} }由上述ThreadLocalMap中的set()方法知道ThreadLocal中Entry下标是通过计算ThreadLocal的hashCode获得了而remove()方法要找到需要移除value所在Entry数组中的下标时也时通过当前ThreadLocal对象的hashCode获的然后找到它的下标之后调用expungeStaleEntry将其value也置为null。我们继续看一下expungeStaleEntry方法的源码 private int expungeStaleEntry(int staleSlot) {Entry[] tab table;int len tab.length;// expunge entry at staleSlottab[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();if (k null) {e.value null;tab[i] null;size--;} else {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; }在expungeStaleEntry()方法中会将ThreadLocal为null对应的value设置为null同时会把对应的Entry对象也设置为null并且会将所有ThreadLocal对应的value为null的Entry对象设置为null这样就去除了强引用便于后续的GC进行自动垃圾回收也就避免了内存泄露的问题。即调用完remove方法之后ThreadLocalMap的结构图如下 在ThreadLocal中不仅仅是remove()方法会调用expungeStaleEntry()方法在set()方法和get()方法中也可能会调用expungeStaleEntry()方法来清理数据。这种设计确保了即使没有显式调用remove()方法系统也会在必要时自动清理不再使用的ThreadLocal变量占用的内存资源。 需要我们特别注意的是尽管ThreadLocal提供了remove这种机制来防止内存泄漏但它并不会自动执行相关的清理操作。所以为了确保资源有效释放并避免潜在的内存泄露问题我们应当在完成对ThreadLocal对象中数据的使用后及时调用其remove()方法。我们最好(也是必须)是在try-finally代码块结构中在finally块中明确地执行remove()方法这样即使在处理过程中抛出异常也能确保ThreadLocal关联的数据被清除从而有利于GC回收不再使用的内存空间避免内存泄漏。 总结 本文探讨了ThreadLocal的工作原理以及其内存泄漏问题及解决策略。ThreadLocal通过为每个线程提供独立的变量副本实现多线程环境下的数据隔离。其内部通过ThreadLocalMap与当前线程绑定利用弱引用管理键值对。但是如果未及时清理不再使用的ThreadLocal变量可能导致内存泄漏尤其是在线程池场景下。解决办法包括在完成任务后调用remove方法移除无用数据。正确理解和使用ThreadLocal能够有效提升并发编程效率但务必关注潜在的内存泄漏风险。 本文已收录于我的个人博客码农Academy的博客专注分享Java技术干货包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等。
http://www.w-s-a.com/news/423/

相关文章:

  • 商品网站怎么做wordpress 表情拉长
  • 商城网站设计费用网络公司怎样推广网站
  • 视频公司的网站设计工图网
  • 免费快速网站十八个免费的舆情网站