ThreadLocal与线程池使用的问题

Posted westlin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal与线程池使用的问题相关的知识,希望对你有一定的参考价值。

感谢博主的这篇分享,见 https://www.cnblogs.com/qifenghao/p/8977378.html

在今天的面试中,突然被考官问了这个问题,当时脱口而出的是 threadlocal容易会有内存泄漏,需要注意remove。其实自己仔细想想,这个回答太过于结果了,没有思考为何要配合线程池的时候,去remove。

 

注意,这里需要你的jdk版本为1.8及以上,否者清将lambda表达式改为匿名内部类

 

问题的版本

 1 public class ThreadLocalAndPool {
 2 
 3     /**
 4      * jdk8 的语法
 5      */
 6     private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0);
 7 
 8     public static int get() {
 9         return variableLocal.get();
10     }
11 
12     public static void remove() {
13         variableLocal.remove();
14     }
15 
16     public static void increment() {
17         variableLocal.set(variableLocal.get() + 1);
18     }
19 
20     public static void main(String[] args) {
21         ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12));
22 
23         for(int i=0;i<5;i++){
24             executorService.execute(()->{
25                 long threadId = Thread.currentThread().getId();
26 
27                 int before = get();
28                 increment();
29                 int after = get();
30                 System.out.println("threadid " + threadId +"  before " + before + ", after " + after);
31             });
32         }
33 
34         executorService.shutdown();
35     }
36 
37 
38 }

得到的结果

threadid 12 before 0, after 1
threadid 13 before 0, after 1
threadid 12 before 1, after 2
threadid 13 before 1, after 2
threadid 12 before 2, after 3

 

这个其实就是threadlocal与线程池使用的问题了,因为threadlocal维护是 Map<Thread,T>这个结构,而线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。

 

修正的版本,就是加一个remove

 1 public class ThreadLocalAndPool {
 2 
 3     /**
 4      * jdk8 的语法
 5      */
 6     private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0);
 7 
 8     public static int get() {
 9         return variableLocal.get();
10     }
11 
12     public static void remove() {
13         variableLocal.remove();
14     }
15 
16     public static void increment() {
17         variableLocal.set(variableLocal.get() + 1);
18     }
19 
20     public static void main(String[] args) {
21         ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12));
22 
23         for(int i=0;i<5;i++){
24             executorService.execute(()->{
25                 try {
26                     long threadId = Thread.currentThread().getId();
27 
28                     int before = get();
29                     increment();
30                     int after = get();
31                     System.out.println("threadid " + threadId +"  before " + before + ", after " + after);
32                 }
33                 finally {
34                     remove();
35                 }
36             });
37         }
38 
39         executorService.shutdown();
40     }
41 
42 
43 }

上面运行的结果如下(不同机器的threadid会有所不同)

threadid 12 before 0, after 1
threadid 13 before 0, after 1
threadid 12 before 0, after 1
threadid 13 before 0, after 1
threadid 13 before 0, after 1

 

以上是关于ThreadLocal与线程池使用的问题的主要内容,如果未能解决你的问题,请参考以下文章

重点知识学习(8.4)--[线程池 , ThreadLocal]

Threadlocal与Tomcat线程池

当ThreadLocal碰上线程池

在使用线程池时应特别注意对ThreadLocal的使用

ThreadLocal遇到线程池时, 各线程间的数据会互相干扰, 串来串去

transmittable-thread-local:解决线程池之间ThreadLocal本地变量传递的问题