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