• 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

  • 相关阅读:
    CLR线程池基础
    何时使用线程
    【转】PowerShell入门(十一):编写脚本模块
    【转】PowerShell入门(十):使用配置文件
    【转】PowerShell入门(九):访问.Net程序集、COM和WMI
    【转】PowerShell入门(八):函数、脚本、作用域
    【转】PowerShell入门(七):管道——在命令行上编程
    【转】PowerShell入门(六):远程操作
    【转】PowerShell入门(五):Cmd命令与PowerShell命令的交互
    【转】PowerShell入门(四):如何高效地使用交互式运行环境?
  • 原文地址:https://www.cnblogs.com/westlin/p/10645217.html
Copyright © 2020-2023  润新知