ThreadLocal是一个线程的局部变量,也就是只有当前线程可以访问,是线程安全的。为每一个线程分配不同的对象,需要在应用层面保证ThreadLocal只起到简单的容器作用。

ThreadLocal类很简单,只有4个方法,它们是如下方法:

  • void set(Object value)设置当前线程的线程局部变量的值。
  • public Object get()该方法返回当前线程所对应的线程局部变量
  • public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显示调用该方法清楚线程的局部变量并不是必须的操作,但它可以加快内存回收速度。
  • protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第一次调用get()或者set()时才会执行,并且仅执行一次。ThreadLocal中的缺省实现直接返回一个null。

ThreadLocal如何保证这些对象只被当前线程访问?先从set()方法说起:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

  在set时先获得当前线程对象,然后通过getMap()方法拿到线程的ThreadLocalMap,并将值存入ThreadLocalMap中。这个map中key是ThreadLocal当前对象,value就是我们需要的值。而在get()方法操作时,自然就是将这个Map中数据拿出来。

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

  get()方法先取得当前线程的ThreadLocalMap对象,然后通过将自己作为key取得内部的实际数据。这样会有个问题:这些变量是维护在Thread类内部的,也就是线程不退出,对象的引用一直存在。Thread退出时的代码如下:

private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
target = null;
/* 加速资源清理 */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}

  因此,使用线程池就意味着当前线程未必会退出(比如固定大小的线程池,线程总是存在的)。这样的话,将一些大的对象放在ThreadLocal中可能会使系统出现内存泄露的可能,设置对象后使用几次不清理它,对象不再有用,但已经无法收回。如果希望及时回收对象,可以使用ThreadLocal.remove()方法将变量移除。对于ThreadLocal变量,手动设置为null,这个ThreadLocal对应的所有线程的局部变量都有可能被收回。下面就是个例子

package com.wyw.xc.test.threadlocal;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class ThreadLocalDemo_Gc { static volatile ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(){
@Override
protected void finalize() throws Throwable{
System.out.println(this.toString()+" is gc");
}
};
static volatile CountDownLatch cd = new CountDownLatch(10000);
public static class ParseDate implements Runnable{ int i = 0;
public ParseDate(int i){
this.i= i;
} @Override
public void run() {
try {
if(tl.get()==null){
tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"){ @Override
protected void finalize() throws Throwable{
System.out.println(this.toString()+ " is gc ");
}
});
System.out.println(Thread.currentThread().getId()+":create SimpleDateFormat");
}
Date t = tl.get().parse("2019-08-04 18:40"+i%60);
} catch (Exception e) {
e.printStackTrace();
}finally{
cd.countDown();
}
}
} public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10000; i++) {
es.execute(new ParseDate(i));
}
cd.await();
System.out.println("mission complete!!");
tl=null;
System.gc();
System.out.println("first GC complete!");
tl = new ThreadLocal<SimpleDateFormat>();
cd = new CountDownLatch(10000);
for (int i = 0; i < 10000; i++) {
es.execute(new ParseDate(i));
}
cd.await();
Thread.sleep(1000);
System.gc();
System.out.println("second GC complete!!");
}
}

  首先线程池中10个线程各自创建了一个SimpleDateFormat对象实例,接着进行第一次GC,可以看到ThreadLocal对象被收回。第二次提交任务时,这次也创建了10个SimpleDateFormat对象,然后进行第二次GC。第二次GC后第一次创建的10个对象全部回收,虽然我们没有手工remove()这些对象,但是系统仍然有可能回收它们。

为每一个线程分配一个独立的对象对系统性能是有帮助的,如果共享对象对于竞争的处理容易引起性能损失,我们还是应该考虑使用ThreadLocal为每个线程分配单独的对象。

多线程(8) — ThreadLocal的更多相关文章

  1. 架构师养成记--6.单例和多线程、ThreadLocal

    一.ThreadLocal 使用wait/notify方式实现的线程安全,性能将受到很大影响.解决方案是用空间换时间,不用锁也能实现线程安全. 来看一个小例子,在线程内的set.get就是thread ...

  2. 【Python】[进程和线程]多进程,多线程,ThreadLocal,进程VS.线程,分布式进程

    1.多进程,multiprocessing模块,   进程间的通信:Queue[队列],Pipes[管子]2.多线程,    注意:线程公用变量,混乱   解决方法Lock:因为只有一个锁,所以当要执 ...

  3. Java多线程之 ThreadLocal

    一.什么是ThreadLocal? 顾名思义它是local variable(线程局部变量).它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副 ...

  4. Java多线程:ThreadLocal

    一.ThreadLocal基础知识 ThreadLocal是线程的一个本地化对象,或者说是局部变量.当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的 ...

  5. 并发和多线程(六)--ThreadLocal

    ThreadLocal是什么? 当使用ThreadLocal修饰变量的时候,ThreadLocal会为每个使用该变量的线程提供独立的变量副本,每个线程可以独立改变自己的副本,而不 影响其他线程的变量副 ...

  6. Java多线程基础-ThreadLocal

    感谢原文作者:Yuicon 原文链接:https://segmentfault.com/a/1190000016705955 序 在多线程环境下,访问非线程安全的变量时必须进行线程同步,例如使用 sy ...

  7. java 多线程(threadlocal)

    package com.example; import java.util.Random; public class App { public static class MyRunnable1 imp ...

  8. java多线程学习-ThreadLocal

    为了凑字,把oracle文档里介绍ThreadLocal抄过来 public class ThreadLocal<T> extends Object This class provides ...

  9. 多线程(二)ThreadLocal

    ThreadLocal public class Demo extends Thread{ static int i = 0; public Integer getNext(){ i++; retur ...

  10. java多线程--------深入分析 ThreadLocal 内存泄漏问题

    前言 ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度.但是如果滥用ThreadLocal,就可能 ...

随机推荐

  1. [提权]mysql中的UDF提权

    由于udf提权是需要构造UDF函数文件的,涉及到了写文件.所以本次实验已经将mysql的配置做了改动:–secure-file-priv=''. 剧情须知: secure_file_priv 为 NU ...

  2. [Shell]Docker remote api未授权访问漏洞(Port=2375)

    0x01 简介 该未授权访问漏洞是因为docker remote api可以执行docker命令,从官方文档可以看出,该接口是目的是取代docker 命令界面,通过url操作docker. Docke ...

  3. 【洛谷】P3188 [HNOI2007]梦幻岛宝珠

    题目描述 给你N颗宝石,每颗宝石都有重量和价值.要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值.  数据范围:N<=100;W<=2^30,并且保证每 ...

  4. zabbix—自动发现端口并监控

    自动批量检查agent开放的端口 PS:如果服务器上的应用都是固定的,不会随机产生的都可以使用自动发现端口来监控:  如果服务器会随机出现端口且每次启动程序都会改变,可以采用第二种方法,来监控指定的端 ...

  5. SQL查询练习题

    1.查询学生"百里守约"的基本信息 select * from students where name='百里守约' 2.查询学生百里守约"或"百里玄策&quo ...

  6. [转载]运行中的DLL自升级

      最近手头有个需求:dll需要注入到某个进程常驻,该dll具备自我升级能力,当发现新的可用版本时,立即Free自己,加载新的.下面是一个实现方案: 开启一个监听线程,从网络上拉新的可用版本,下载放到 ...

  7. 肿瘤免疫疗法 | 细胞治疗和PD1/PDL1 | Tumor immunotherapy | cell therapy

    人类肿瘤治疗史上的里程碑无疑一定有一座是肿瘤免疫疗法的. 而肿瘤免疫疗法的主要两大领域,细胞治疗以及以PD1/PDL1为代表的免疫检查点抑制剂都在飞速发展. 目前,已经有5种抗PD1/PDL1抗体药物 ...

  8. GIS地理工具案例教程——合并选中图层

    GIS地理工具案例教程--合并选中图层 商务合作,科技咨询,版权转让:向日葵,135-4855__4328,xiexiaokui#qq.com 功能:并图层列表中 描述:对图层列表中选中图层进行合并. ...

  9. Android访问WCF服务

    原文链接:http://www.cnblogs.com/VinC/archive/2011/02/24/1964049.html 本章目的: 用Wcf建立可以上Android可以访问的数据服务, 数据 ...

  10. 001 centos7下安装kibana

    Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索.查看交互存储在Elasticsearch索引中的数据. 所以,在安装完ES之后,这里再安装一下kibana.方便后面学 ...