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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM