感谢博主的这篇分享,见 https://www.cnblogs.com/qifenghao/p/8977378.html
在今天的面试中,突然被考官问了这个问题,当时脱口而出的是 threadlocal容易会有内存泄漏,需要注意remove。其实自己仔细想想,这个回答太过于结果了,没有思考为何要配合线程池的时候,去remove。
注意,这里需要你的jdk版本为1.8及以上,否者清将lambda表达式改为匿名内部类
问题的版本
public class ThreadLocalAndPool { /**
* jdk8 的语法
*/
private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0); public static int get() {
return variableLocal.get();
} public static void remove() {
variableLocal.remove();
} public static void increment() {
variableLocal.set(variableLocal.get() + 1);
} public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12)); for(int i=0;i<5;i++){
executorService.execute(()->{
long threadId = Thread.currentThread().getId(); int before = get();
increment();
int after = get();
System.out.println("threadid " + threadId +" before " + before + ", after " + after);
});
} executorService.shutdown();
} }
得到的结果
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
public class ThreadLocalAndPool { /**
* jdk8 的语法
*/
private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0); public static int get() {
return variableLocal.get();
} public static void remove() {
variableLocal.remove();
} public static void increment() {
variableLocal.set(variableLocal.get() + 1);
} public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(2,2,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>(12)); for(int i=0;i<5;i++){
executorService.execute(()->{
try {
long threadId = Thread.currentThread().getId(); int before = get();
increment();
int after = get();
System.out.println("threadid " + threadId +" before " + before + ", after " + after);
}
finally {
remove();
}
});
} executorService.shutdown();
} }
上面运行的结果如下(不同机器的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与线程池使用的问题的更多相关文章
-
ThreadLocal遇到线程池时, 各线程间的数据会互相干扰, 串来串去
最近遇到一个比较隐蔽而又简单地问题,在使用ThreadLocal时发现出现多个线程中值串来串去,排查一番,确定问题为线程池的问题,线程池中的线程是会重复利用的,而ThreadLocal是用线程来做Ke ...
-
当ThreadLocal碰上线程池
ThreadLocal使用 ThreadLocal可以让线程拥有本地变量,在web环境中,为了方便代码解耦,我们通常用它来保存上下文信息,然后用一个util类提供访问入口,从controller层到s ...
-
ThreadLocal,LinkedBlockingQueue,线程池 获取数据库连接2改进
package com.ctl.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQL ...
-
0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal
什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...
-
线程池与Threadlocal
线程池与Threadlocal 线程池: 线程池是为了使线程能够得到循环的利用,线程池里面养着一些线程,有任务需要使用线程的时候就往线程池里抓线程对象出来使用.线程池里的线程能够重复使用,所以在资源上 ...
-
ThreadLocal 遇上线程池的问题及解决办法
ThreadLocal 称为线程本地存储,它为每一个使用它的线程提供一个其值(value)的副本.可以将 ThreadLocal<T> 理解成 Map<Thread, T>,即 ...
-
ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)
ThreadLocal 也可以跟踪一个请求,从接收请求,处理请求,到返回请求,只要线程不销毁,就可以在线程的任何地方,调用这个参数,这是百度二面的题目,参考: Threadlocal 传递参数(百度二 ...
-
线程池-Threadlocal
ThreadLoclc初衷是线程并发时,解决变量共享问题,但是由于过度设计,比如弱引用的和哈希碰撞,导致理解难度大.使用成本高,反而成为故障高发点,容易出现内存泄露,脏数据.贡献对象更新等问题.单从T ...
-
线程池 Threadlocal 使用注意
线程池中的线程是重复使用的,即一次使用完后,会被重新放回线程池,可被重新分配使用. 因此,ThreadLocal线程变量,如果保存的信息只是针对一次请求的,放回线程池之前需要清空这些Threadloc ...
随机推荐
-
【T-SQL基础】01.单表查询-几道sql查询题
概述: 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础 ...
-
BZOJ 4551: [Tjoi2016&;Heoi2016]树
4551: [Tjoi2016&Heoi2016]树 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 748 Solved: 394[Subm ...
-
Python--关于set
慕课网<Pyrhon入门>学习笔记 1.set 特性 set 持有一系列元素,这一点和 list 很像,但是set的元素没有重复,而且是无序的,这点和 dict 的 key很像. 可以将s ...
-
【转载】应读者强烈要求给出《超容易的Linux系统管理入门书》一书的主要知识点
刚开始了一篇连载,收到广大Linux爱好者的反馈,非常欣慰.大家对Linux学习感到很迷茫,不知道学哪些内容,如何学习? <超容易的Linux系统管理入门书>一书是腾讯Linux专家在腾讯 ...
-
【HDOJ】1158 Employment Planning
简单DP. #include <cstdio> #include <cstring> #include <cstdlib> #include <climits ...
-
关于Resharper的使用经验
发现Resharper这东西真的有点累赘,重构也是,一开始用会有很多莫名其妙的提示.现在的项目用了Resharper,js是很方便,有定位功能,但连TypeScript的js都有了.
-
2019.04.26 mongaodb
打开服务 mongod.exe --dbpath E:\Develop\mongodb_64\data\db 在安装和打开服务之前要建一个db 文件的存储位置 然后打开服务 打开服务之后 打开图 ...
-
codeblock不能调试
问题:codeblock 不能调试,如下图: 解决方法: 首先,项目的保存路径不能是中文路径. 其次,必须创建工程. 最后,build选项里select target选成debug codebloc ...
-
使用FireFox插件RESTClient、HttpRequester模拟http(get post)请求
我们写好一个接口后,需要进行测试.有时我们会写一个html表单提交,无疑增加了工作量,尤其是当参数比较多或者传json或xml数据时,效率更是大大降低.我们可以使用基于FireFox的RESTClie ...
-
读写文件:每次读入大文件里的一行、读写.CSV文件
读文件: 传统的读法.所有读出,按行处理: fp=open("./ps.txt", "r"); alllines=fp.readlines(); fp.clos ...