ThreadLocal的set方法和get方法,从set方法开始:
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取线程的局部变量
if (map != null)//判断map是否存在
map.set(this, value);//set值 key是当前ThreadLocal对象 value是value
else
createMap(t, value);//否则 创建一个map设置值
}
     get方法:
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取线程的局部变量map
if (map != null) {//当map存在时
ThreadLocalMap.Entry e = map.getEntry(this);//获取entry(键值对)
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;//返回值
}
}
return setInitialValue();//返回null
}
     在了解了ThreadLocal的内部实现后,我看到一个问题,那就是这些变量是维护在Thread类内部的,这也意味着只要线程不退出,对象的引用将一直存在,
     当线程退出是,Thread类会进行一些清理工作,其中就包括清理ThreadLocalMap.下面是具体实现:在Thread类内部:
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
     因此,如果我们使用线程池,那就意味着当前线程未必会退出,(比如固定大小的线程池,线程总是存在的,) 如果这样,将一些大大的对象设置到ThreadLocal中,可能会使系统出现内存在泄漏,
     此时,你希望及时的GC,最好使用ThreadLocal.remove()方法将这个变量移除,就像我们习惯性的关闭数据库链接一样,如果你确定不需要这个对象了,那么就应该告诉虚拟机,把他回收掉,防止内存泄漏,
     另外一种有趣的情况是JDK也可能允许你想释放普通变量一样释放ThreadLocal.比如,我么你有时候为了加入GC.会特意写出类似obj=null之类的代码.如果这么做,obj所指向的对象就会更容易地垃圾回收器发现,从而加速回收,
     同理,如果对于ThreadLocal的变量,我们也手动将其设置null.比如t1=null.那么这个ThreadLocal对应的所有线程的局部变量都有可能被回收,我们写个小例子来看一看奥秘:
public class ThreadLocalDemo_Gc {
static volatile ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>() {
@Override
protected void finalize() throws Throwable { //重载了finalize() 当对象在GC时,打印信息
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 (t1.get() == null) {
t1.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 = t1.get().parse("2016-12-19 19:29:" + i % 60);
} catch (ParseException e) {
e.printStackTrace();
} finally {
cd.countDown();//完成 计数器减1
}
}
} 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!!");
t1 = null;
System.gc();
System.out.println("first GC complete!!");
t1 = new ThreadLocal<>();
cd = new CountDownLatch(1000);
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!!");
}
}
     输出结果如下:
     在主函数Main中,先后进行了2次任务提交,每次10000个任务,在第一次任务提交后,我们t1设置为null 接着进行了一次gc,接着我们进行了第二次任务提交,完成后在进行一次gc,
     注意这些输出,.我们发现了当t1被设置为null时候,第一次gc 回收了.接着提交第二次任务,这次我们也是创建了10个线程,可以看到,虽然我们手动remove()这些对象,但是系统依然有可能回收他们.
     要了解这里的回收机制,我们需要进一步了解Thread.ThreadLocalMap的实现,之前我们说过,ThreadLocalMap是一个类似HashMap的东西,更精确地说,他更加类似WeakHashMap.
     ThreadLocalMap的实现使用了弱引用,弱引用是比强引用弱的多的引用,java在虚拟机回收时,如果发现若引用,就会立即回收,ThreadLocalMap内部由一系列Entry构成,每一个entry都是WeakReferenc<ThreadLocal>:

static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
      这里的参数k就是map的key,v就是Map的value.其中k也就是ThreadLocal实例,作为弱引用使用(super(k)就是调用了WeakReferenc的构造函数,)因此,索然这里使用了ThreadLocal作为map的key,但是实际上,他并不是真的持有ThreadLocal的引用,而当THreadLocal的外部引用被回收时,ThreadLocalMap中的key就会变成null.当系统进行ThreadLocalMap清理时,就会自然将这些垃圾数据回收, 

ThreadLocal的实现原理(读书笔记)的更多相关文章

  1. <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步(1)

    <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步 并发问题是所有问题的基础,也是操作系统设计的基础.并发包括很多设计问题,其中有进程间通信,资源共享与竞争,多个 ...

  2. 深入探索Android热修复技术原理读书笔记 —— 热修复技术介绍

    1.1 什么是热修复 对于广大的移动开发者而言,发版更新是最为寻常不过的事了.然而,如果你 发现刚发出去的包有紧急的BUG需要修复,那你就必须需要经过下面这样的流程: 这就是传统的更新流程,步骤十分繁 ...

  3. 深入探索Android热修复技术原理读书笔记 —— 代码热修复技术

    在前一篇文章 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍中,对热修复技术进行了介绍,下面将详细介绍其中的代码修复技术. 1 底层热替换原理 在各种 Android 热修复方案中 ...

  4. 深入探索Android热修复技术原理读书笔记 —— 资源热修复技术

    该系列文章: 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍 深入探索Android热修复技术原理读书笔记 -- 代码热修复技术 1 普遍的实现方式 Android资源的热修复,就 ...

  5. 深入探索Android热修复技术原理读书笔记 —— so库热修复技术

    热修复系列文章: 深入探索Android热修复技术原理读书笔记 -- 热修复技术介绍 深入探索Android热修复技术原理读书笔记 -- 代码热修复技术 深入探索Android热修复技术原理读书笔记 ...

  6. LOMA280保险原理读书笔记

    LOMA是国际金融保险管理学院(Life Office Management Association)的英文简称.国际金融保险管理学院是一个保险和金融服务机构的国际组织,它的创建目的是为了促进信息交流 ...

  7. Spark基本工作流程及YARN cluster模式原理(读书笔记)

    Spark基本工作流程及YARN cluster模式原理 转载请注明出处:http://www.cnblogs.com/BYRans/ Spark基本工作流程 相关术语解释 Spark应用程序相关的几 ...

  8. 通信原理读书笔记:常规AM调制的功率

    Proakis,通信系统原理,p101: 两个不同频率正弦和的功率为其功率的和. 计算功率时,和的平方展开后会出现两个正弦乘积项,按积化和差展开后在公共周期内积分为零.

  9. ThreadLocal的简单使用(读书笔记)

         从ThreadLocal的名字上可以看到,这是一个线程局部变量,也就是说,只有当前线程可以访问,既然是只有当前线程可以访问的数据,自然是线程安全的. public class ThreadL ...

随机推荐

  1. KVM(三)I/O 全虚拟化和准虚拟化

    在 QEMU/KVM 中,客户机可以使用的设备大致可分为三类: 1. 模拟设备:完全由 QEMU 纯软件模拟的设备. 2. Virtio 设备:实现 VIRTIO API 的半虚拟化设备. 3. PC ...

  2. Django-ContentType

    背景:学位课.专题课.价格策略(每一种课程(学位课和专题课下可分为不同的种类的课程)在不同学习时间内的价格不同) 例如:如何将课程表与价格策略表关联起来: 用外键是可以将课程表和价格策略表关联起来的, ...

  3. poj 2826(好坑,线段相交问题)

    An Easy Problem?! Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11576   Accepted: 176 ...

  4. 关于MYSQL表记录字段换行符回车符处理

    http://hualong.iteye.com/blog/1933023 今天遇到一个非常奇葩的问题,数据库表中明明有值却查询不不出来,而然一次从单元格中复制到sql中,发现右侧单引号换行了,我初步 ...

  5. Laravel5.5配置使用redis

    1.安装redis linux上redis的安装与配置 2.安装redis客户端 composer require predis/predis或者安装 PhpRedis PHP 扩展brew inst ...

  6. codeforces 671C

    题意定义f(l,r)为去掉[l,r]部分后剩下的数任意两个数的最大公约数的最大值 现在求f(l,r)的和 由于每个数ai最大只有200000,因此我们穷举因子x,记录以其为因子的a[i]的i值并按i升 ...

  7. 【NOIP2007】字符串展开解题报告

    描述 Description 在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或“4-8”的子串,我们就把它当作一种简写,输出时,用 ...

  8. boost::Circular Buffer

    boost.circular_buffer简介 很多时候,我们需要在内存中记录最近一段时间的数据,如操作记录等.由于这部分数据记录在内存中,因此并不能无限递增,一般有容量限制,超过后就将最开始的数据移 ...

  9. Linux下 编译C++/C以及常用的几种命令(ubuntu)

    http://blog.csdn.net/bob1993_dev/article/details/45973919

  10. Linux CentOS下Python+robot framework环境搭建

    转载自:http://blog.sina.com.cn/s/blog_13cc013b50102vof1.html 操作系统环境:CentOS 6.5-x86_64 下载地址:http://www.c ...