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

西安专业网站开发联系电话东营做网站哪家好

西安专业网站开发联系电话,东营做网站哪家好,注册一个自己的网站,保定网站建设在日常 Java 开发中,ThreadLocal 是一个非常实用却又容易被误解的工具。它常被用于线程隔离变量,避免同步开销。然而,网上各种内存泄露问题是不是让你对它的使用有些担忧? 其实没那么可怕,看完这篇文章,你可…

在日常 Java 开发中,ThreadLocal 是一个非常实用却又容易被误解的工具。它常被用于线程隔离变量,避免同步开销。然而,网上各种内存泄露问题是不是让你对它的使用有些担忧? 其实没那么可怕,看完这篇文章,你可以大胆用起来了。


一、ThreadLocal 简介

ThreadLocal 是 Java 提供的一种用于线程本地存储的机制。它可以为每一个线程提供一个变量的副本,使得每个线程都可以独立地修改自己的副本,而不会影响其他线程的变量。

常见的使用方式如下:

private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal =ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

在多线程环境下,这样做的好处是避免了 SimpleDateFormat 非线程安全的问题,无需使用锁来进行同步。


二、如何理解 ThreadLocal?

很多初学者会误以为 ThreadLocal 是一个线程,它“存”在线程里。其实更准确的说法是:每个线程内部都维护着一个 ThreadLocalMap,这个 Map 的 key 是当前的 ThreadLocal 实例,value 是你设置的值。

简言之,ThreadLocal 是变量的“访问入口”,而真正的存储空间,是在线程内部的一个专属 Map 中。这样设计的好处在于:

  • 避免多线程并发读写一个变量引发的竞态问题;
  • 每个线程独占自己的数据副本,无需加锁,性能更高;
  • 提高了代码的封装性和可读性。

三、ThreadLocal 的底层原理

ThreadLocal 底层实现依赖 Thread 类中的一个成员变量:

ThreadLocal.ThreadLocalMap threadLocals;

当我们调用 ThreadLocal#set 方法时,实际上就是往当前线程的 ThreadLocalMap 中存放键值对。这个 Map 并不是标准的 java.util.Map,它是 ThreadLocal 的静态内部类,专为这个目的设计。

核心结构大致如下:

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;}
}

几个关键点:

  • Entry 的 key 是 ThreadLocal,但是 弱引用(WeakReference)
  • value 是我们存储的对象;
  • 使用弱引用的目的,是为了防止内存泄漏(但如果使用不当,仍然会泄漏);

ThreadLocal;Thread,ThreadLocalMap关系

ThreadLocalMap是ThreadLocal的静态内部类,本质是一个hash表,单独实现的哈希表,与java集合中的map实现不一样。ThreadLocalMap内部维护了一个Entry键值对,key是ThreadLocal对象的引用,值是任意对象。其中entry还继承了弱引用。
Thread线程对象内部维护了一个ThreadLocalMap,用来保存当前线程绑定的变量。
如下图:


四、ThreadLocal 引发的内存泄漏问题

ThreadLocal的开发者Doug Lea, 为了减少内存泄漏的可能,做了两种努力:

  • 使用弱引用作为key
  • 尽可能的清除脏Entry

接下来我们分析下,这两种操作是如何尽量减少内存泄露的可能。

弱引用作为key

首先介绍什么是弱引用:

  1. 强: 不回收
  2. 软: 内存不够才回收 (适合做缓存)
  3. 弱: GC时垃圾回收线程发现就回收
  4. 虚: 跟踪对象被垃圾回收的状态。虚引用必须与 ReferenceQueue 配合使用
    有如下代码:

内存图:


当方法结束后栈帧弹栈,那么对应的图中 的强引用自然就没了, 而ThreadLocal身上,此时只有一个弱引用,
当GC时, ThreadLocal对象就会被回收。

因此,设计为弱引用,可以尽量减少内存泄漏。

为什么是尽量呢, 因为即便是GC回收了ThreadLocal对象, Entry还在,Entry的k指向了Null, 导致V永远无法被访问到,此时ThreadLocalmap还挂在线程对象上, 如果该线程是池里面的某个线程,则线程一直在, 导致Entry一直在, 所以内存泄漏了

尽可能的清除脏Entry

GC回收了ThreadLocal对象, Entry还在,Entry的k指向了Null, 此时ThreadLocalMap上会存在很多key为null,但value不为null的情况。
ThreadLocal会在 set() get(), remove方法时, 寻找K为null的Entry, 然后清楚。

源码如下:


expungeStaleEntry这个方法, get remove都会调用到。

但是我们无法保证,我们是否还会再调用这三个方法, 所以最佳实践是,用完就清除

正确做法:

  • 在合适时机手动调用 remove() 方法清除线程变量。
try {threadLocal.set(...);// 业务逻辑
} finally {threadLocal.remove();
}

五、ThreadLocal 的典型应用场景

1. 数据库连接管理

场景:在 Web 应用中,每个请求可能需要独立的数据库连接,避免多线程共用一个连接导致的并发问题。
示例

public class ConnectionHolder {private static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> {try {return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb");} catch (SQLException e) {throw new RuntimeException("Failed to create connection", e);}});public static Connection getConnection() {return connectionHolder.get();}public static void remove() {connectionHolder.remove();}
}// 使用示例:在 DAO 层获取当前线程的独立连接
Connection conn = ConnectionHolder.getConnection();
// 使用后清理,防止内存泄漏(尤其在线程池中)
ConnectionHolder.remove();

2. 线程不安全的工具类

场景:如 SimpleDateFormat 非线程安全,通过 ThreadLocal 为每个线程提供独立实例。
示例

public class DateUtils {private static ThreadLocal<SimpleDateFormat> dateFormatHolder = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));public static String format(Date date) {return dateFormatHolder.get().format(date);}
}// 多线程调用安全
String dateStr = DateUtils.format(new Date());

3. 用户会话(Session)管理

场景:在 Web 应用中,保存当前登录用户的信息,避免层层传递参数。
示例

public class UserContext {private static ThreadLocal<User> currentUser = new ThreadLocal<>();public static void setCurrentUser(User user) {currentUser.set(user);}public static User getCurrentUser() {return currentUser.get();}public static void clear() {currentUser.remove();}
}// 在拦截器中设置用户信息
UserContext.setCurrentUser(loggedInUser);
// 业务层直接获取
User user = UserContext.getCurrentUser();
// 请求结束时清理
UserContext.clear();

4. 分页参数传递

场景:在分页查询中,将当前页码和每页数量存储在 ThreadLocal 中,避免跨层传递参数。
示例

public class PageContext {private static ThreadLocal<Integer> pageNum = new ThreadLocal<>();private static ThreadLocal<Integer> pageSize = new ThreadLocal<>();public static void setPage(int num, int size) {pageNum.set(num);pageSize.set(size);}public static int getPageNum() {return pageNum.get();}public static void clear() {pageNum.remove();pageSize.remove();}
}// 控制层设置分页参数
PageContext.setPage(1, 10);
// DAO 层直接使用
int offset = PageContext.getPageNum() * PageContext.getPageSize();

5. 事务管理

场景:在事务框架中,标记当前线程是否处于事务中。
示例

public class TransactionManager {private static ThreadLocal<Boolean> isInTransaction = ThreadLocal.withInitial(() -> false);public static void begin() {isInTransaction.set(true);}public static boolean isActive() {return isInTransaction.get();}public static void end() {isInTransaction.remove();}
}

六、TransmittableThreadLocal 的原理与优势

随着异步编程和线程池的广泛使用,ThreadLocal 的局限性逐渐暴露:它无法在线程池中传递上下文。因为线程是复用的,新的任务拿不到旧任务设置的 ThreadLocal 数据。

为了解决这个问题,阿里开源了一个增强版本:TransmittableThreadLocal(TTL)。

工作原理:

TTL 的核心思路是在任务提交到线程池之前,将上下文复制一份,包装成新的 Runnable/Callable 对象,并在执行时恢复上下文。这样上下文就得以在线程池中“传递”。

简化流程如下:

  1. 提交任务前,提取当前线程的 ThreadLocal 值;
  2. 创建一个 TtlRunnableTtlCallable 包装原任务;
  3. 执行时,先恢复 ThreadLocal 环境,再执行原始任务;
  4. 执行后清除环境,防止污染其他线程。

示例用法:

ExecutorService executor = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(4));TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
ttl.set("traceId-123");executor.submit(() -> {// 可以访问 ttl.get(),拿到 "traceId-123"
});

通过 TTL,我们可以轻松地在异步场景中实现线程上下文的传递,尤其适合微服务、日志追踪、链路监控等场景。


总结

ThreadLocal 是一个功能强大的工具,其核心价值在于线程封闭(Thread Confinement),将某些需隔离的资源绑定到线程生命周期,简化多线程编程复杂度。不要因为害怕内存泄露而不敢用, 只要我们用完就清除,就不会泄露,大胆用起来吧!

最后

如果文章对你有帮助,点个免费的赞鼓励一下吧!关注gzh:加瓦点灯, 每天推送干货知识!


文章转载自:

http://06TiXNbg.Lfqtp.cn
http://tSITvMa2.Lfqtp.cn
http://g5oCcSSE.Lfqtp.cn
http://u6NfWFRn.Lfqtp.cn
http://aOeyQ6pa.Lfqtp.cn
http://RNVnfBLb.Lfqtp.cn
http://duzP9lHx.Lfqtp.cn
http://CTYY8TyZ.Lfqtp.cn
http://mg3WUIYq.Lfqtp.cn
http://eN9SNDuJ.Lfqtp.cn
http://7GoSBJ93.Lfqtp.cn
http://aavLogKj.Lfqtp.cn
http://JswaOSrU.Lfqtp.cn
http://iEfAFM8a.Lfqtp.cn
http://dDROTb3I.Lfqtp.cn
http://gkV06OI6.Lfqtp.cn
http://G5BqC8hG.Lfqtp.cn
http://PEwIuIgP.Lfqtp.cn
http://z1dMmIai.Lfqtp.cn
http://CM4rmdX8.Lfqtp.cn
http://B6yW5GpG.Lfqtp.cn
http://lJ5vzosW.Lfqtp.cn
http://GOhApWZ6.Lfqtp.cn
http://eVFUoWlX.Lfqtp.cn
http://JlYOaRuP.Lfqtp.cn
http://9VVBmtzu.Lfqtp.cn
http://DGPweRBM.Lfqtp.cn
http://ePa7dsTZ.Lfqtp.cn
http://Vg8fDLIs.Lfqtp.cn
http://Y1sPipOU.Lfqtp.cn
http://www.dtcms.com/wzjs/746192.html

相关文章:

  • 白云做网站SEO上海做网站的月薪
  • 网站规划与建设模板专业建站推荐
  • 网站链接设计小城镇建设网站的观点
  • 网站如何做seo的动画设计就业方向和发展前景
  • 网页ui设计网站济宁网站运营策略
  • 衡阳网站定制网站站点
  • 国际设计师网站有哪些1小时前俄乌战况消息
  • 怎么看到网站开发时间网站建设成本
  • 网站建设续费是那些驻马店网站建设熊掌号
  • 八角网站建设旅游网站设计说明
  • 成都cms建站52麻将官方网站做代理
  • 哪个网站做免费小程序商城网站建设需要多少钱
  • 团建网站建设wordpress阿里云建站
  • 自己做的网站能上传吗国内营销策划公司排名
  • 设计素材网站酷p搜狗识图
  • 做盗版小说网站赚钱嘛云南建设厅网站首页
  • 长春网站制作企业做网站的必要
  • 怎样做分销网站百度联盟添加网站
  • 网站的推广一般有什么方式便宜购 网站建设
  • 云南省城市建设培训中心网站上海公司注册地址可以是住宅吗
  • 北京规划网站网站建设招聘需求
  • 百度推广代理怎么加盟电子商务沙盘seo关键词
  • 照明公司网站制作公司网络组建方案设计
  • 做网站什么系统简单网站建设案例收费吗
  • 马鞍山做网站的公司78做网站的公司怎么转型
  • 网站制作对公司的作用优秀seo外包平台
  • html手机网站条形码生成器在线制作图片
  • 读经典做临床报名网站东莞seo排名优化公司
  • 网站调研怎样做网站建设怎样接业务
  • 没有外贸网站 如果做外贸宁波最好的推广平台