ThreadLocal用法详解和原理
一、用法
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
/**
* ThreadLocal用法
* @author coshaho
*
*/
public class MyThreadLocal
{
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(){
/**
* ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
*/
@Override
protected Object initialValue()
{
System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
return null;
}
}; public static void main(String[] args)
{
new Thread(new MyIntegerTask("IntegerTask1")).start();
new Thread(new MyStringTask("StringTask1")).start();
new Thread(new MyIntegerTask("IntegerTask2")).start();
new Thread(new MyStringTask("StringTask2")).start();
} public static class MyIntegerTask implements Runnable
{
private String name; MyIntegerTask(String name)
{
this.name = name;
} @Override
public void run()
{
for(int i = 0; i < 5; i++)
{
// ThreadLocal.get方法获取线程变量
if(null == MyThreadLocal.threadLocal.get())
{
// ThreadLocal.et方法设置线程变量
MyThreadLocal.threadLocal.set(0);
System.out.println("线程" + name + ": 0");
}
else
{
int num = (Integer)MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(num + 1);
System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
if(i == 3)
{
MyThreadLocal.threadLocal.remove();
}
}
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} } public static class MyStringTask implements Runnable
{
private String name; MyStringTask(String name)
{
this.name = name;
} @Override
public void run()
{
for(int i = 0; i < 5; i++)
{
if(null == MyThreadLocal.threadLocal.get())
{
MyThreadLocal.threadLocal.set("a");
System.out.println("线程" + name + ": a");
}
else
{
String str = (String)MyThreadLocal.threadLocal.get();
MyThreadLocal.threadLocal.set(str + "a");
System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());
}
try
{
Thread.sleep(800);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} }
}
运行结果如下:
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2: 0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程StringTask1: a
线程StringTask2: a
线程StringTask1: aa
线程StringTask2: aa
线程IntegerTask1: 1
线程IntegerTask2: 1
线程StringTask1: aaa
线程StringTask2: aaa
线程IntegerTask2: 2
线程IntegerTask1: 2
线程StringTask2: aaaa
线程StringTask1: aaaa
线程IntegerTask2: 3
线程IntegerTask1: 3
线程StringTask1: aaaaa
线程StringTask2: aaaaa
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask2: 0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
线程IntegerTask1: 0
二、原理
线程共享变量缓存如下:
Thread.ThreadLocalMap<ThreadLocal, Object>;
1、Thread: 当前线程,可以通过Thread.currentThread()获取。
2、ThreadLocal:我们的static ThreadLocal变量。
3、Object: 当前线程共享变量。
我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocal, Object>,然后根据当前ThreadLocal获取当前线程共享变量Object。
ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。
这种存储结构的好处:
1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
关于ThreadLocalMap<ThreadLocal, Object>弱引用问题:
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
ThreadLocal用法详解和原理的更多相关文章
- ThreadLocal用法详解和原理(转)
本文转自https://www.cnblogs.com/coshaho/p/5127135.html 感谢作者 一.用法 ThreadLocal用于保存某个线程共享变量:对于同一个static Thr ...
- ThreadLocal类详解:原理、源码、用法
以下是本文目录: 1.从数据库连接探究 ThreadLocal 2.剖析 ThreadLocal 源码 3. ThreadLocal 应用场景 4. 通过面试题理解 ThreadLocal 1.从数据 ...
- es6的promise用法详解
es6的promise用法详解 promise 原理 promise是es6的异步编程解决方案, 是es6封装好的对象: 一个promise有三种状态:Pending(进行中).Resolved(已完 ...
- JS逗号运算符的用法详解
逗号运算符的用法详解 注意: 一.由于目前正在功读JavaScript技术,所以这里拿JavaScript为例.你可以自己在PHP中试试. 二.JavaScript语法比较复杂,因此拿JavaScri ...
- jQuery 事件用法详解
jQuery 事件用法详解 目录 简介 实现原理 事件操作 绑定事件 解除事件 触发事件 事件委托 事件操作进阶 阻止默认事件 阻止事件传播 阻止事件向后执行 命名空间 自定义事件 事件队列 jque ...
- 1:CSS中一些@规则的用法小结 2: @media用法详解
第一篇文章:@用法小结 第二篇文章:@media用法 第一篇文章:@用法小结 这篇文章主要介绍了CSS中一些@规则的用法小结,是CSS入门学习中的基础知识,需要的朋友可以参考下 at-rule ...
- Java语言Socket接口用法详解
Socket接口用法详解 在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...
- js原生之scrollTop、offsetHeight和offsetTop等属性用法详解
scrollTop.offsetHeight和offsetTop等属性用法详解:标题中的几个相关相关属性在网页中有这大量的应用,尤其是在运动框架中,但是由于有些属性相互之间的概念比较混杂或者浏览器兼容 ...
- lsof 命令用法详解
lsof 命令用法详解 作用 用于查看你进程开打的文件,打开文件的进程,进程打开的端口(TCP.UDP).找回/恢复删除的文件.是十分方便的系统监视工具,因为lsof命令需要访问核心内存和各种文件,所 ...
随机推荐
- kali linux 安装 matlab2016Rb
分享安装包: https://pan.baidu.com/s/1hrBd3Li 密码:u9q3 由于Linux版的分为两个镜像,需要挂载后合并: mount R2016b_glnxa64_dvd1.i ...
- pycharm安装numpy和scipy(window)
win 10,64位,python3.6 用的pycharm,依次点file-settings-project-project interpreter,出现下图,(一般按正常安装流程,Package里 ...
- 词向量之Word2vector原理浅析
原文地址:https://www.jianshu.com/p/b2da4d94a122 一.概述 本文主要是从deep learning for nlp课程的讲义中学习.总结google word2v ...
- 【转】辽宁北斗TV直播源
原帖地址:http://bbs.asiatvro.com/thread-1156600-1-1.html 感谢 Qarclub 辽宁卫视,http://lnhlslive.hongshiyun.com ...
- ffmpeg 版本升级到 4.0 增加 libaom 库 [AOMedia 的 AV1 视频编码格式]
win10 中交叉编译 libaom 时 注意事项 libaom 源代码 下载 git -c "http.proxy=ip:port" clone https://aomedia. ...
- MySQL(介绍,安装,密码操作,权限表)
一.数据库介绍1.数据库相关概念 a.支持并发 b.锁的问题 c.对客户端请求进行认证 d.存取效率(降低IO次数) 数据库服务器(本质就是一个台计算机,该计算机之上安装 ...
- ansible笔记(6):常用模块之命令类模块
ansible笔记():常用模块之命令类模块 command模块 command模块可以帮助我们在远程主机上执行命令 注意:使用command模块在远程主机中执行命令时,不会经过远程主机的shell处 ...
- Go断言
用Go语言提供的类型检测方法,同时也可作为断言的解决方案: package main import ( "fmt" "reflect" ) type MyStr ...
- springboot:集成fastjson(教训)
网上有很多介绍,主要有两种. 1.在启动类中注入bean /** * 集成fastjson * * @return */ @Bean public HttpMessageConverters fast ...
- Day8--------------RPM包管理
nginx.tar.gz:源码,编译安装 RPM:redhat package manage,二进制 增.删.查 1.增 package------>ls|less------------> ...