TheadLocal传递参数一定要记得Remove,否则会产生Bug,TheadLocal踩坑记:
使用ThreadLocal保存用户信息,代码执行过程中报错,导致Remove代码没有执行,那么下个用户进来,发现ThreadLocal有数据,直接取来用,则会出现用户信息错乱的情况。下个用户之所以能取到上个用户/线程的数据,是因为所有的web服务器底层都是使用的线程池,线程都是复用的。代码里使用Java线程池也是一样的。这个问题相当严重。
若要避免这个问题,一是尽量避免使用ThreadLocal,如果使用,请确保Remove代码一定能执行到;二是Set Get逻辑那里一定要考虑上个线程没有Remove的情况,比如在Set前总是先Remove下。
我们用一段代码模拟一下出问题的场景:
首先设置一个线程,往ThreadLocal里设置值。
class MyThread implements Runnable {
@Override
public void run() {
ThreadPoolTest.data.set("hello");
System.out.println(Thread.currentThread().getId() + "已设置值");
}
}
然后设置一个取值的线程
class MyThread2 implements Runnable {
@Override
public void run() {
Object name = ThreadPoolTest.data.get();
System.out.println(Thread.currentThread().getId() + "获取到的值==" + name);
}
}
最后用一个核心数为1的线程池去执行线程
public class ThreadPoolTest {
public static ThreadLocal data = new ThreadLocal<String>();
public static void main(String[] args) {
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(10000);
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
// 只能并发一个
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 10, 30, TimeUnit.SECONDS, workQueue, handler);
MyThread thread = new MyThread();
threadPool.execute(thread);
for (int i = 0; i < 2; i++) {
MyThread2 thread2 = new MyThread2();
threadPool.execute(thread2);
}
}
}
结果打印:
13已设置值
13获取到的值==hello
13获取到的值==hello