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

小伙做钓鱼网站 背警方带走河南今日重大新闻

小伙做钓鱼网站 背警方带走,河南今日重大新闻,电子商务网站建设方案书,大连html5网站建设目录 什么是 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/124168.html

相关文章:

  • 常德网站建设哪家权威网址查询
  • 动态网站开发语言都有哪些网上营销网站
  • vps的网站打不开廊坊百度快照优化哪家服务好
  • 网站的建设价格营销策略有哪些4种
  • 宁波网站优化价格甘肃seo网站
  • 投票网站怎么制作seo能干一辈子吗
  • 建设政府门户网站的意义有哪些超级优化
  • 重庆网站建设培训机构学费网络推广文案
  • 网站栏目关键词付费推广有几种方式
  • 泰顺做网站google seo实战教程
  • 网站建设代理开发科技企业服务长沙seo网络营销推广
  • 广东微信网站制作多少钱啥是网络推广
  • 做物流的网站都有什么风险谷歌浏览器下载手机版中文
  • 地图网站模板广告投放优化师
  • wordpress适合下载站的主题可以推广网站
  • 百度云wordpress怎么搭建网站51链
  • phpcms移动端网站怎么做搜索引擎优化的目的是
  • 做公众号app网站app吗兰州seo优化公司
  • 做网站优化如何遍文章网络营销成功案例
  • 网站备案查询到什么资料曹操论坛seo
  • 系部网站建设中期检查总结福建省人民政府门户网站
  • 页面好看的教育类网站模板如何在百度提交自己的网站
  • 软件公司网站建设乐事薯片软文推广
  • 怎么做网站首页关键词城市更新论坛破圈
  • 网站投票怎么做查询网址域名ip地址
  • 网站 页面 结构搜狗收录提交入口网址
  • 那里可以做工作室做网站搜狗收录提交入口
  • 苏州免费推广的网站海南百度推广公司有哪些
  • 百度网站建设工资用网站模板建站
  • 网站建设文化策划书上海seo推广方法