问题:当线程池与ThreadLocal
共用时,ThreadLocal
读取数据出现错乱。
问题验证:
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
final ThreadLocal<String> threadLocal = new ThreadLocal<String>();
Runnable runnable = () -> {
System.out.println(threadLocal.get());
threadLocal.set(Thread.currentThread().getName() + ": name");
};
for (int i = 0; i < 6; i++) {
executorService.submit(runnable);
}
executorService.shutdown();
}
结果输出:
null
null
pool-1-thread-1: name
pool-1-thread-2: name
pool-1-thread-1: name
pool-1-thread-2: name
由此可以看到,当我们在线程池中获取ThreadLocal
内容时出现错乱。这是因为线程池会复用线程对象,与线程对象绑定的类的静态属性ThreadLocal
变量也会被重用,这就导致一个线程可能获取到其他线程的ThreadLocal
值。
非线程池情况下:
public static void main(String[] args) {
final ThreadLocal<String> threadLocal = new ThreadLocal<String>();
for (int i = 0; i < 6; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(threadLocal.get());
threadLocal.set(Thread.currentThread().getName() + ": name");
}
});
thread.start();
}
}
结果输出:
null
null
null
null
null
null
内容获取正常
总结:
ThreadLocal
属于线程,于线程创建而生,线程结束而自然销毁,本来是没什么问题的。在线程池中,线程并不会用完了而销毁,而是会放回到线程池中,这时候ThreadLocal
里存放的数据还在。下一个请求来了从线程池中拿到这个线程,就会获取到上次请求存放的数据。
所以每次使用完ThreadLocal
就调用remove
将其删掉。