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

企业网站建设综合实训心得体会一千字上海培训机构整顿

企业网站建设综合实训心得体会一千字,上海培训机构整顿,网站建设深圳市,专业建设主考学校是什么意思目录 什么是 ThreadLocal?1.应用场景2.如何使用 底层原理1.数据存储1.1Thread 、ThreadLocal、ThreadLocalMap、Entry 之间的关系 2.set()3.get()4.remove() 存在问题1.内存堆积1.1复现过程1.2存在问题1.3解决方法 2.线程池复用导致数据污染 为什么 ThreadLocal 是弱…

目录

    • 什么是 ThreadLocal?
      • 1.应用场景
      • 2.如何使用
    • 底层原理
      • 1.数据存储
        • 1.1Thread 、ThreadLocal、ThreadLocalMap、Entry 之间的关系
      • 2.set()
      • 3.get()
      • 4.remove()
    • 存在问题
      • 1.内存堆积
        • 1.1复现过程
        • 1.2存在问题
        • 1.3解决方法
      • 2.线程池复用导致数据污染
    • 为什么 ThreadLocal 是弱引用?
      • 1.如果 key 是强引用
      • 2.采用弱引用,解决内存泄漏
      • 3.为什么 value 不是弱引用?
      • 4.正确的使用方式
    • ThreadLocal 与 Synchronized 的区别
    • 应用场景
      • 1.用户身份信息
    • Reference

什么是 ThreadLocal?

ThreadLocal 是 Java 提供的一种用于线程本地存储的工具类,它可以为每个线程提供独立的变量副本,从而实现线程隔离。ThreadLocal 主要用于在多线程环境下存储线程独有的数据,避免多个线程间共享变量带来的数据一致性问题。

1.应用场景

应用场景说明
数据库连接管理绑定 Connection到线程,避免同步问题
用户身份管理线程隔离 Session,存储用户信息
日志追踪存储 Trace ID,方便日志分析
日期格式化避免 SimpleDateFormat线程不安全问题
线程计数器统计当前线程的执行次数
线程池数据传递使用 TransmittableThreadLocal解决线程池数据丢失问题

2.如何使用

  1. 创建 ThreadLocal

创建了一个 ThreadLocal 变量 localVariable,任何一个线程都能并发访问 localVariable。

public static ThreadLocal<String> localVariable = new ThreadLocal<>();
  1. 新增数据,线程可以在任何地方写入数据
localVariable.set("测试数据");
  1. 读取数据,线程在任何地方都能够读取数据
localVariable.get();
  1. 删除数据
localVariable.remove();

底层原理

1.数据存储

ThreadLocal 变量的值存储在 Thread 内部的 ThreadLocalMap ,不是 ThreadLocal 本身。

public class ThreadLocal<T> {static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {  // 弱引用类型Object value;   // 强引用Entry(ThreadLocal<?> k, Object v) {super(k);   // ThreadLocal 类型作为key,弱引用value = v; }}private Entry[] table;}
}
  1. ThreadLocalMap 采用 线性探测法 解决哈希冲突
  2. 弱引用Entry 继承 WeakReferenceThreadLocal 对象被回收时,键会变为 null,避免内存泄漏
  3. 每个 Thread 维护一个 ThreadLocalMap 实例,键是 ThreadLocal,值是变量
1.1Thread 、ThreadLocal、ThreadLocalMap、Entry 之间的关系
[Thread]|v
[ThreadLocalMap] (threadLocals)|v
[Entry[]] (table)|+--> [Entry1] --> key: WeakReference<ThreadLocalA>|                 value: ValueA|+--> [Entry2] --> key: WeakReference<ThreadLocalB>value: ValueB

Thread 线程可以拥有多个 ThreadLocal 维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)

2.set()

public void set(T value) {Thread t = Thread.currentThread();  // 获取当前线程ThreadLocalMap map = getMap(t);   // 获取线程的ThreadLocalMapif (map != null) {map.set(this, value);} else {createMap(t, value);}
}
  1. 先获取当前线程 ThreadLocalMap
  2. 如果 map 存在,直接存入 ThreadLocalMap
  3. 否则创建一个新的 ThreadLocalMap

3.get()

public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {T result = (T)e.value;return result;}}return setInitialValue();
}
  1. 通过 Thread.currentThread() 获取当前线程
  2. ThreadLocalMap 获取当前 ThreadLocal 变量
  3. 若不存在,则调用 setInitialValue() 赋初值
private T setInitialValue() {T value = initialValue();   // 返回null值Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}// 检查当前对象 (this) 是否是 TerminatingThreadLocal 的实例// TerminatingThreadLocal 是 ThreadLocal 的子类()if (this instanceof TerminatingThreadLocal) {TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);}return value;
}

4.remove()

public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null) {m.remove(this);}
}

存在问题

1.内存堆积

public class ThreadLocal<T> {static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {  // 弱引用Object value;Entry(ThreadLocal<?> k, Object v) {super(k);   // ThreadLocal 类型作为key。value = v;}}private Entry[] table;}
}

由于 ThreadLocalMap.Entry 采用 弱引用 存储 ThreadLocal,但值 value强引用,可能导致:

  1. ThreadLocal 被回收后,Entry.key 变为 null
  2. value 仍然存在,无法被回收,造成 内存泄漏

GC 时,

强引用:永不回收(除非手动置 null

软引用:只有在内存不足时才会清理软引用对象

弱引用:发现弱引用后,会立即回收,不会管内存是否充足。

虚引用:对象被回收后通知

表面上 keyvalue 使用的是同一个 ThreadLocal, 实际上使用的 value 却是自己独有的一份。

1.1复现过程
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
threadLocal.set(new Object());  // 存入一个对象threadLocal = null;  // ThreadLocal 被置为 null,意味着没有强引用指向它

执行后的引用关系

  1. ThreadLocal不再有外部强引用,GC 会回收它。
  2. ThreadLocalMap 中仍然有 Entry,只是 Entry.key 变成了 null(因为 ThreadLocal<?> 是弱引用,已经被 GC 回收)。
  3. Entry.value 仍然是一个强引用,它存储的对象不会被 GC 回收,导致内存泄漏
ThreadLocalMap (ThreadLocal 被 GC 回收后)
----------------------------------------
| Entry (null)  ->  value (强引用对象) |
----------------------------------------
1.2存在问题
  1. Entry 的 key 是弱引用,当 ThreadLocal 实例被回收后,key 变为 null
  2. 对应的 value 仍然被强引用链Thread -> ThreadLocalMap -> Entry -> value 保持;
  3. 线程池场景下线程长期存活,那么 ThreadLocalMap也不会被销毁,导致 value导致累积大量无效 Entry,一直占用内存
1.3解决方法

使用完 ThreadLocal 后手动调用 remove()

threadLocal.remove(); // 清理当前线程的 ThreadLocal 变量

这样 ThreadLocalMap 就会手动删除 Entry,避免 value 残留。

2.线程池复用导致数据污染

线程池会复用线程,导致 ThreadLocal 变量未清理时,被下一个任务访问:

ExecutorService executor = Executors.newFixedThreadPool(2);
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);for (int i = 0; i < 5; i++) {executor.submit(() -> {threadLocal.set((int) (Math.random() * 100));System.out.println(Thread.currentThread().getName() + " -> " + threadLocal.get());});
}

如果不 remove(),可能会导致脏数据问题。


为什么 ThreadLocal 是弱引用?

1.如果 key 是强引用

假设 ThreadLocalMap.Entry 采用强引用存储 ThreadLocal<?>

static class Entry {  ThreadLocal<?> key;  // 改为强引用Object value;  Entry(ThreadLocal<?> k, Object v) {  key = k;  value = v;  }  
}

那么,即使开发者手动置 ThreadLocal 变量为 null,它仍然无法被 GC 回收

ThreadLocal<Object> threadLocal = new ThreadLocal<>();
threadLocal.set(new Object());threadLocal = null; // 置为 null,开发者期望 GC 回收 ThreadLocal
System.gc(); // 但由于 key 仍然是强引用,GC 不会回收 ThreadLocal

此时:

  1. ThreadLocalMap仍然持有 ThreadLocal 的强引用,即使 threadLocal = null,它仍然存活在 ThreadLocalMap 里,无法被 GC。
  2. 长期运行的线程(如线程池)会一直持有 ThreadLocal,导致内存泄漏

根本原因

  1. ThreadLocalMap 的生命周期 == 线程的生命周期。
  2. 如果线程是长期存活的(如线程池),那么 ThreadLocalMap 也不会被销毁
  3. 即使 ThreadLocal 变量已经超出作用域,仍然有强引用,无法被回收,导致内存泄漏

2.采用弱引用,解决内存泄漏

JDK 设计者为了避免 ThreadLocalMap 内存泄漏,采用了 WeakReference<ThreadLocal<?>>

static class Entry extends WeakReference<ThreadLocal<?>> {  Object value;  Entry(ThreadLocal<?> k, Object v) {  super(k);  // key 使用弱引用value = v;  }  
}
  1. ThreadLocal<?>弱引用,当开发者不再使用 ThreadLocal 变量时,GC 会自动回收它
  2. ThreadLocal 被回收后,Entry.key 变为 null,但 value 仍然存在(强引用)。
  3. ThreadLocalMap** 在下次 set()/getEntry() 操作时会清理 key == nullEntry,确保 value 也被释放**。

3.为什么 value 不是弱引用?

如果 value 也是 WeakReference,那么可能会导致 ThreadLocal.get()返回 null,影响正常业务逻辑:

threadLocal.set(new Object()); // 假设 value 也是弱引用
System.gc(); 
threadLocal.get(); // 可能返回 null,因为 value 也被回收了

这样 ThreadLocal无法正常使用,导致业务代码异常。因此:

  1. key使用弱引用,允许 ThreadLocal 被 GC 回收,防止内存泄漏。
  2. value使用强引用,保证业务逻辑正常运行,避免 get() 返回 null

4.正确的使用方式

  1. 建议将ThreadLocal变量定义为static类型。这样设置可以延长ThreadLocal实例的生命周期,因为存在对它的强引用,从而确保了ThreadLocal对象不会被垃圾回收机制过早地清理掉。这种做法有助于在任何时候都能够通过ThreadLocal持有的弱引用来访问到其内部Entry中的值,并及时调用remove()方法来清除这些值,进而有效避免内存泄漏问题的发生。
  2. 在每次使用完ThreadLocal后,务必显式调用其remove()方法以清空存储的数据。这一措施对于预防潜在的内存泄漏至关重要。

ThreadLocal 与 Synchronized 的区别

  1. 相同点: ThreadLocal 和 Synchronized 都是用于处理多线程并发问题。
  2. 不同点(数据访问):
    1. ThreadLocal 适合“每个线程独立的变量“,为每个线程都提供一个副本,保证每个线程访问数据的隔离。
    2. Synchronized 适合“多个线程共享同一个变量”,使用锁机制,保证数据在某一时刻只能被一个线程访问。

应用场景

  1. 数据库连接管理(每个线程独立 Connection
  2. 用户身份信息(每个线程存储当前用户 Session
  3. 日志追踪(每个请求独立的 Trace ID
  4. 线程安全的工具类(如 SimpleDateFormat
  5. 等等

1.用户身份信息

这段代码实现了一个Spring MVC的拦截器(HandlerInterceptor),主要用于处理用户登录状态的验证。具体来说,它通过检查请求中的Token来判断用户是否已经登录,并将登录用户的信息存储在ThreadLocal中,以便在当前线程的后续处理中使用。

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();/*** 处理请求前拦截*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 前后端分离会有option刺探请求,查看网络是否正常if (HttpMethod.OPTIONS.toString().equalsIgnoreCase(request.getMethod())) {response.setStatus(HttpStatus.NO_CONTENT.value());return true;}// 获取tokenString accessToken = request.getHeader("token");if (!StringUtils.isNotBlank(accessToken)) {// 有些情况,请求头中token可能为空,就从参数中获取tokenaccessToken = request.getParameter("token");}if (StringUtils.isNotBlank(accessToken)) {Claims claims = JWTUtil.checkJWT(accessToken);if (claims == null) {// 未登录CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));return false;}// 用户已经登录Long accountNo = Long.valueOf(claims.get("account_no").toString());String headImg = claims.get("head_img").toString();String username = claims.get("username").toString();String mail = claims.get("mail").toString();String phone = claims.get("phone").toString();String auth = claims.get("auth").toString();LoginUser loginUser = LoginUser.builder().accountNo(accountNo).headImg(headImg).username(username).mail(mail).phone(phone).auth(auth).build();//request.setAttribute("loginUser", loginUser);// 将参数传入当前线程threadLocal.set(loginUser);return true;}CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN));return false;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 当前线程完成后,移除threadLocal.remove();}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}
}

Reference

  1. 史上最全ThreadLocal 详解(一)
  2. ChatGPT
http://www.dtcms.com/wzjs/136817.html

相关文章:

  • 国外做vj的网站qq群推广链接
  • wordpress添加qq聊天北京seo网络优化招聘网
  • 延边住房和城乡建设局网站口碑营销策略
  • 个人网站备案号被注销了网页设计排版布局技巧
  • 网站建设的基本需求有哪些方面软文推广500字
  • 网站建设公司做的网站时事新闻热点
  • 专业的wap网站开发人工智能培训机构排名
  • 建平县营商环境建设局网站公司网站如何seo
  • 嘉兴网站排名优化公司代运营网店公司
  • 邯郸市做网站的公司西安百度seo排名
  • 做课件赚钱网站在哪里seo综合查询是什么
  • 四川网站建设 湖南岚鸿直通车推广计划方案
  • 分类网站上怎么做锚文本百度联盟广告收益
  • 高埗网站建设公司丈哥seo博客工具
  • 宣武郑州阳网站建设seo外包上海
  • 寻找做网站seo网站营销公司哪家好
  • 梧州网站优化如何做网站关键词优化
  • 网页设计学生作业步骤seo短期培训班
  • 微信小程序游戏修改器seo外链发布软件
  • 专业的移动网站建设公司排名关键词优化工具
  • 一个域名怎么用来做多个网站地推团队联系方式
  • 高唐网站建设优化师培训机构
  • 网站建设福州最好电商seo搜索引擎优化
  • 青海建设厅网站特种作业网站查询工具
  • 做百度网站找谁军事新闻头条
  • 建站产品手机优化大师下载2022
  • 个人建站项目产品营销推广
  • 官方app网站手机优化
  • 网站建设网站维护的具体内容是什么链接提交入口
  • 郑州政府网站搭建seo的含义