ThreadLocal与线程池使用的问题
感谢博主的这篇分享,见 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 ...
- Java线程池进阶
线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...
- 0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal
什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...
- 线程池与Threadlocal
线程池与Threadlocal 线程池: 线程池是为了使线程能够得到循环的利用,线程池里面养着一些线程,有任务需要使用线程的时候就往线程池里抓线程对象出来使用.线程池里的线程能够重复使用,所以在资源上 ...
- ThreadLocal 遇上线程池的问题及解决办法
ThreadLocal 称为线程本地存储,它为每一个使用它的线程提供一个其值(value)的副本.可以将 ThreadLocal<T> 理解成 Map<Thread, T>,即 ...
- ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)
ThreadLocal 也可以跟踪一个请求,从接收请求,处理请求,到返回请求,只要线程不销毁,就可以在线程的任何地方,调用这个参数,这是百度二面的题目,参考: Threadlocal 传递参数(百度二 ...
- 线程池-Threadlocal
ThreadLoclc初衷是线程并发时,解决变量共享问题,但是由于过度设计,比如弱引用的和哈希碰撞,导致理解难度大.使用成本高,反而成为故障高发点,容易出现内存泄露,脏数据.贡献对象更新等问题.单从T ...
随机推荐
- content+animation实现loading效果
<dot></dot> dot { display: inline-block; height: 1em; line-height: 1; vertical-align: -. ...
- 获取spring的IOC核心容器,并根据id获取对象
public class Client { /** * 获取spring的IOC核心容器,并根据id获取对象 * ApplicationContext的三个常用实现类 * classPathXmlAp ...
- Django 2.0 官方文档翻译
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/' ...
- Java并发编程之美之并发编程线程基础
什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就 ...
- python&django 实现页面中关联查询小功能(基础篇)
效果 实现效果图如下,根据过滤条件查询相关信息. 知识点 1.配置URL,在路由中使用正则表达式 2.过滤查询 代码 setting.py from django.contrib import adm ...
- Java基础 -- String,StringBuilder,StringBuffer三者的区别
结论 1-String,StringBuilder,StringBuffer 之间的区别主要是在两个方面,即运行速度和线程安全这两方面: 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:St ...
- Date——js 获取当前日期到之后一个月30天的日期区间
var dateList = []; let startDate = new Date(); let endDate = new Date(); endDate.setDate(startDate.g ...
- 【easy】235. Lowest Common Ancestor of a Binary Search Tree
题意大概是,找两个节点的最低公共祖先 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNod ...
- ogg 单表拆分合并进程
metalink文档:1320133.1和1512633.1 map scott.emp1, target scott.emp1 ,FILTER(@RANGE(1,3)); --拆分 map sco ...
- 使用Jenkins部署asp.net core
首先盗图一张 1.在Github上创建一个测试仓库 2.创建基于Asp.net Core的MVC项目 运行解决方案,验证代码没有问题,然后将代码迁入Github 3.添加Docker支持 项目--&g ...