ThreadLocal及InheritableThreadLocal的原理剖析
我们知道,线程的不安全问题,主要是由于多线程并发读取一个变量而引起的,那么有没有一种办法可以让一个变量是线程独有的呢,这样不就可以解决线程安全问题了么。其实JDK已经为我们提供了ThreadLocal这个东西。
◆
ThreadLocal基本使用
◆
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal 的主要方法有这么几个:
1 |
initialValue 初始化 |
下面来看一个简单的使用代码示例:
1 |
public class ThreadLocalDemo {
|
上方代码使用了10个线程循环对一个threadLocal的值进行一千次的加法,如果我们不知道ThreadLocal的原理的话我们可能会觉得最后打印的值一定是1000、2000、3000。。10000或者是线程不安全的值。
但是如果你执行这段代码你会发现最后打印的都是1000。
◆
ThreadLocal原理剖析
◆
现在我们来看一下ThreadLocal是如何实现为每个线程单独维护一个变量的呢。
先来看一下初始化方法。
1 |
protected T initialValue() {
|
initialValue 默认是返回空的,所以为了避免空指针问题重写了这个方法设置了默认返回值为0,但是呢,虽然这个方法好像是设置默认值的,但是还没有生效,具体请接着往下看。
1 |
public void set(T value) {
|
我们可以看到set方法首先会获取当前线程,然后通过一个getMap方法获取了ThreadLocalMap,接着来看一下这个map是怎么来的呢。
1 |
ThreadLocalMap getMap(Thread t) {
|
这个map是在Thread类维护的一个map,下方是Thread类维护的此变量。默认这个map是空的。
1 |
ThreadLocal.ThreadLocalMap threadLocals = null; |
接着往下看代码,如果获取的时候map不为空,则通过set方法把Thread类的threadLocals变量更新。如果是第一次创建的时候则初始化Thread的threadLocals变量。
下方是createMap的代码:
1 |
void createMap(Thread t, T firstValue) {
|
接下来看个get方法就比较容易理解了。
1 |
public T get() {
|
注意关注最后的一个return,看到调用的这个方法名我们就可以发现这个ThreadLocal的初始化原来是当第一调用get方法时如果还没有被set的时候才会去获取initialValue 方法的返回值。
1 |
private T setInitialValue() {
|
◆
使用ThreadLocal最应该注意的事项
◆
首先来看一下线程退出的办法:
1 |
private void exit() {
|
我们看到当线程结束的时候上方第7行会把ThreadLocal的值制为空,这个东西本身是没问题的。但是,如果你是使用的线程池,这个问题可就大了!!!
要知道线程池里的线程执行完一个任务之后紧接着下一个,这中间线程可不会结束,下一个任务获得Thread的值可是上一个任务的遗留数据。
下面是这个问题的示例代码:
1 |
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
|
执行这段代码你就会发现同样的操作在线程池里已经得不到一样的结果了。想要解决这种问题也很简单,只需要把ThreadLocal的值在线程执行完清空就可以了。把第14行注释的代码放开再执行以下你就明白了。
◆
InheritableThreadLocal
◆
其实ThreadLocal还有一个比较强大的子类InheritableThreadLocal,它呢可以把父线程生成的变量传递给子线程。
下面来看一下代码示例:
1 |
public class InheritableThreadLocalDemo {
|
执行代码会发现程序输出全是1024,这就是因为InheritableThreadLocal吧在主线程设置的值24传递到了那10个子线程中。
◆
InheritableThreadLocal原理剖析
◆
接下来我们来看一下InheritableThreadLocal为什么可以实现这种功能呢。
InheritableThreadLocal是ThreadLocal的子类,
与ThreadLocal相同的set方法
1 |
public void set(T value) {
|
不同点是InheritableThreadLocal重写了createMap方法,将值赋值给了线程的inheritableThreadLocals变量。
1 |
void createMap(Thread t, T firstValue) {
|
再跟进去Thread类的源码看inheritableThreadLocals变量你会发现:我去,这不是跟Threadlocal一样么,同样初始值为null,线程退出的时候清空。没错,就是这样的。也就是说它其实也是一个线程私有的变量,ThreadLocal的功能它是都有的。
那么它又是怎么把父线程的变量传递到子线程的呢?
接着看Thread的构造方法
1 |
public Thread() {
|
一路追踪init方法你会看见这段代码:
1 |
private void init(ThreadGroup g, Runnable target, String name, |
仔细观察倒数第5行到倒数第二行你就明白了。
本文所有源码https://github.com/shiyujun/syj-study-demo
ThreadLocal及InheritableThreadLocal的原理剖析的更多相关文章
- InheritableThreadLocal类原理简介使用 父子线程传递数据详解 多线程中篇(十八)
上一篇文章中对ThreadLocal进行了详尽的介绍,另外还有一个类: InheritableThreadLocal 他是ThreadLocal的子类,那么这个类又有什么作用呢? 测试代码 p ...
- 66、Spark Streaming:数据处理原理剖析与源码分析(block与batch关系透彻解析)
一.数据处理原理剖析 每隔我们设置的batch interval 的time,就去找ReceiverTracker,将其中的,从上次划分batch的时间,到目前为止的这个batch interval ...
- ThreadLocal的使用及原理解析
# 基本使用 JDK的lang包下提供了ThreadLocal类,我们可以使用它创建一个线程变量,线程变量的作用域仅在于此线程内.<br />用2个示例来展示一下ThreadLocal的用 ...
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...
- ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行
ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...
- 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】
原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...
- 【Xamarin 跨平台机制原理剖析】
原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ...
- iPhone/Mac Objective-C内存管理教程和原理剖析
http://www.cocoachina.com/bbs/read.php?tid-15963.html 版权声明 此文版权归作者Vince Yuan (vince.yuan#gmail.com)所 ...
- 【Xamain 跨平台机制原理剖析】
原文:[Xamain 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原生 ...
随机推荐
- 软件工程作业-(second)
一.选择开发工具 自己以前一直都用VS,又由于微软对Microsoft Visual Studio 更新,因此本次自己采用 Visual Studio 2019 来作为开发工具.如下图是安装后的界面: ...
- 1、阿里云ECS内部机器端口被100.117.90段的ip疯狂扫描导致业务异常
故障现象: 解决方案: 1.临时解决 iptables -I INPUT -s 100.117.0.0/12 -j DROP 2.后续解决 提交工单,寻找阿里服务. 后续定位是以前配置过的SLB在搞鬼 ...
- python 逻辑运算 ‘and’ ,'or' 在实战中的作用,代替if语句。
彩票程序:课上方法:import random # 生成一个随机两位数 作为一个中奖号码luck_num = random.randint(10,99)print(luck_num)luck_num_ ...
- java微信获取经纬度转换为高德坐标小结
https://blog.csdn.net/dragon974539495/article/details/78894499
- keras安装-【老鱼学keras】
为何要用keras? 两个字:简单. Keras让深度学习像搭建积木一样方便地来进行,使前面的tensorflow能够更加方便地使用. 虽然还有其它更多的理由,比如:Keras 支持多个后端引擎,不会 ...
- mysql 分库分表转
分表是分散数据库压力的好方法. 分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库. 当然,首先要知道什么情况下,才需要分表.个人觉得单表记录条数达到百万到千万 ...
- java简单框架设计
设计框架包可以作为一个工具给大家用,需要有完全不同设计思路给出来,不同于我们去做一个web服务.网站. 或者一个业务微服务,需要从原来使用视角转换成一个构建者视角. 框架或者工具,更多是框架来管理或者 ...
- webstorm2018.2.3激活
一,简介 WebStorm 是jetbrains公司旗下一款JavaScript 开发工具.目前已经被广大中国JS开发者誉为"Web前端开发神器"."最强大的HTML5编 ...
- 在Github上为项目添加多个用户
点击项目目录中的Settings 点击Collaborators 添加后,合作者会受到确认邮件,等待合作者确认后,合作者就可以提交了 添加组织 在Settings中找到Organizations 点击 ...
- centos7安装kubeadm
安装配置docker v1.9.0版本推荐使用docker v1.12, v1.11, v1.13, 17.03也可以使用,再高版本的docker可能无法正常使用. 测试发现17.09无法正常使用,不 ...