感谢博主的这篇分享,见 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与线程池使用的问题的更多相关文章

  1. ThreadLocal遇到线程池时, 各线程间的数据会互相干扰, 串来串去

    最近遇到一个比较隐蔽而又简单地问题,在使用ThreadLocal时发现出现多个线程中值串来串去,排查一番,确定问题为线程池的问题,线程池中的线程是会重复利用的,而ThreadLocal是用线程来做Ke ...

  2. 当ThreadLocal碰上线程池

    ThreadLocal使用 ThreadLocal可以让线程拥有本地变量,在web环境中,为了方便代码解耦,我们通常用它来保存上下文信息,然后用一个util类提供访问入口,从controller层到s ...

  3. ThreadLocal,LinkedBlockingQueue,线程池 获取数据库连接2改进

    package com.ctl.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQL ...

  4. Java线程池进阶

    线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...

  5. 0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal

    什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...

  6. 线程池与Threadlocal

    线程池与Threadlocal 线程池: 线程池是为了使线程能够得到循环的利用,线程池里面养着一些线程,有任务需要使用线程的时候就往线程池里抓线程对象出来使用.线程池里的线程能够重复使用,所以在资源上 ...

  7. ThreadLocal 遇上线程池的问题及解决办法

    ThreadLocal 称为线程本地存储,它为每一个使用它的线程提供一个其值(value)的副本.可以将 ThreadLocal<T> 理解成 Map<Thread, T>,即 ...

  8. ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)

    ThreadLocal 也可以跟踪一个请求,从接收请求,处理请求,到返回请求,只要线程不销毁,就可以在线程的任何地方,调用这个参数,这是百度二面的题目,参考: Threadlocal 传递参数(百度二 ...

  9. 线程池-Threadlocal

    ThreadLoclc初衷是线程并发时,解决变量共享问题,但是由于过度设计,比如弱引用的和哈希碰撞,导致理解难度大.使用成本高,反而成为故障高发点,容易出现内存泄露,脏数据.贡献对象更新等问题.单从T ...

随机推荐

  1. content+animation实现loading效果

    <dot></dot> dot { display: inline-block; height: 1em; line-height: 1; vertical-align: -. ...

  2. 获取spring的IOC核心容器,并根据id获取对象

    public class Client { /** * 获取spring的IOC核心容器,并根据id获取对象 * ApplicationContext的三个常用实现类 * classPathXmlAp ...

  3. Django 2.0 官方文档翻译

    from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/' ...

  4. Java并发编程之美之并发编程线程基础

    什么是线程 进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程至少有一个线程,进程的多个线程共享进程的资源. java启动main函数其实就 ...

  5. python&django 实现页面中关联查询小功能(基础篇)

    效果 实现效果图如下,根据过滤条件查询相关信息. 知识点 1.配置URL,在路由中使用正则表达式 2.过滤查询 代码 setting.py from django.contrib import adm ...

  6. Java基础 -- String,StringBuilder,StringBuffer三者的区别

    结论 1-String,StringBuilder,StringBuffer 之间的区别主要是在两个方面,即运行速度和线程安全这两方面: 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:St ...

  7. Date——js 获取当前日期到之后一个月30天的日期区间

    var dateList = []; let startDate = new Date(); let endDate = new Date(); endDate.setDate(startDate.g ...

  8. 【easy】235. Lowest Common Ancestor of a Binary Search Tree

    题意大概是,找两个节点的最低公共祖先 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNod ...

  9. ogg 单表拆分合并进程

    metalink文档:1320133.1和1512633.1 map scott.emp1, target scott.emp1 ,FILTER(@RANGE(1,3));  --拆分 map sco ...

  10. 使用Jenkins部署asp.net core

    首先盗图一张 1.在Github上创建一个测试仓库 2.创建基于Asp.net Core的MVC项目 运行解决方案,验证代码没有问题,然后将代码迁入Github 3.添加Docker支持 项目--&g ...