感谢博主的这篇分享,见
在今天的面试中,突然被考官问了这个问题,当时脱口而出的是 threadlocal容易会有内存泄漏,需要注意remove。其实自己仔细想想,这个回答太过于结果了,没有思考为何要配合线程池的时候,去remove。
注意,这里需要你的jdk版本为1.8及以上,否者清将lambda表达式改为匿名内部类
问题的版本
1 public class ThreadLocalAndPool { 2 3 /** 4 * jdk8 的语法 5 */ 6 private static ThreadLocalvariableLocal = 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 1threadid 12 before 1, after 2threadid 13 before 1, after 2threadid 12 before 2, after 3
这个其实就是threadlocal与线程池使用的问题了,因为threadlocal维护是 Map<Thread,T>这个结构,而线程池是对线程进行复用的,如果没有及时的清理,那么之前对该线程的使用,就会影响到后面的线程了,造成数据不准确。
修正的版本,就是加一个remove
1 public class ThreadLocalAndPool { 2 3 /** 4 * jdk8 的语法 5 */ 6 private static ThreadLocalvariableLocal = 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 1threadid 12 before 0, after 1threadid 13 before 0, after 1threadid 13 before 0, after 1