Java ThreadLocal 怎么用
早上刚到公司的小周就犯了难——昨天写的用户登录模块,明明测试时没问题,今天却出现“两个用户登录后,信息互相串了”的bug。盯着代码看了半小时,他终于发现是线程间变量共享惹的祸:两个请求用了同一个线程,把彼此的用户信息搞混了。这时候,一个叫ThreadLocal的工具,就能帮他解决大问题。
ThreadLocal是什么?像给每个线程准备专属抽屉
要讲清楚ThreadLocal,得先从“共享变量的麻烦”说起。就像办公室里的公共文件柜,谁都能放、谁都能拿,要是有人忘拿自己的文件,下一个人很可能拿错。而ThreadLocal的作用,就是给每个用它的线程,单独准备一个“专属小抽屉”。

举个生活化的例子:公司的茶水间有公共水杯,大家轮流用,总有人忘记刷杯子;后来行政给每个人发了专属的个人水杯,每个人自己用自己的,就不会弄混了。这里的“个人水杯”,就对应ThreadLocal;每个线程,就是每个用杯子的人;每个线程的专属变量,就是杯子里装的水,只有线程自己能拿到。
怎么用ThreadLocal?三步就能上手
用ThreadLocal的操作特别简单,完全不用复杂的配置,跟着这三步走就行:
第一步,先创建一个ThreadLocal,指定你要存的变量类型。比如小周要存用户信息,就可以这么写:
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
就像先买好一批统一尺寸的“专属抽屉”,专门放用户信息的盒子。
第二步,往抽屉里放东西,也就是给当前线程赋值。比如用户登录成功后,把用户信息存进去:
userThreadLocal.set(currentUser);
这就像把今天的外卖放到自己的个人抽屉里,只有你能拿到。
第三步,需要用的时候,从抽屉里取。比如后面要查当前登录用户的ID,直接拿就行:
User currentUser = userThreadLocal.get();
不用再去公共柜里翻找,也不会拿到别人的东西。
用的时候要注意什么?别让抽屉变成“垃圾场”
虽然ThreadLocal好用,但如果用不对,也会出问题。最常见的坑就是“内存泄漏”——就像你用了个人水杯,但用完没把里面的水倒掉,一直留在那里,时间长了水杯里堆满旧水,就占了位置。
为什么会这样?因为ThreadLocal的静态变量,会一直被线程引用。如果线程一直不结束(比如Web服务器的线程池,线程是循环用的),存进去的用户信息就会一直留在内存里,没法被清理。
解决方法也很简单:用完抽屉里的东西,一定要清空。比如用户退出登录后,调用userThreadLocal.remove();,就像用完水杯把里面的水倒掉,保持干净。如果是刚学的新手,最好每次set之后,在finally块里加remove,避免漏删。
它和普通变量的区别?一句话说清
很多人会问:既然普通变量也能存,为什么要用ThreadLocal?举个例子就懂:如果把用户信息存在普通的静态变量里,就像公共文件柜,两个人同时拿,肯定会撞。而ThreadLocal的变量,每个线程都有自己的副本,就像每个人的个人水杯,不会互相干扰。
小周按照这个方法修改代码后,测试再也没出现用户信息串的问题。其实不止登录模块,ThreadLocal还常用来存数据库连接、事务信息这些需要线程独享的内容,是Java里解决线程安全问题的小工具,虽然不起眼,但用对了能省很多麻烦。