ThreadLocal可以解决并发问题吗

Easter79
• 阅读 505

前言

到底什么是线程的不安全?为什么会存在线程的不安全?线程的不安全其实就是多个线程并发的去操作同一共享变量没用做同步所产生意料之外的结果。那是如何体现出来的呢?我们看下面的一个非常经典的例子:两个操作员同时操作同一个银行账户,A操作员存钱,100B操作员取钱50。我们看一下流程。

ThreadLocal可以解决并发问题吗

两个操作员同时处理,没用做同步这个时候我们发现银行账户最终余额剩余950元,在我们想的最终结果银行账户应该剩余1000+100-50=1050元,在执行过程中我们没有加锁,最终导致了运行结果偏离预期。那么如何解决的?一般的解决措施就是加锁,加同步锁所以这就需要使用者一定要知道锁是什么。我们来看一下加锁之后的效果是不是我们所预期的。

ThreadLocal可以解决并发问题吗

在添加同步锁后我们可以看到,A操作员和B操作员同时去操作账户,但是A先抢占到资源,所以B就只能等待A操作员释放锁才能去操作银行账户,那么最终结果是我们所预期的吗?答案是的。

同步的话一般都是加锁,如果现在我想创建多个线程每个线程都是访问的自己的变量呢?各个线程之间毫无关联?

答案是有的。

ThreadLocal问题

ThreadLocal是JDK提供的,它提供了线程本地变量。什么是线程本地变量呢?其实就是你创建了一个Threadlocal变量,每个访问Threadlocal变量的线程都有一个本地副本。我们看下面的图:

ThreadLocal可以解决并发问题吗 从上面看出你创建一个ThreadLocal变量,每个访问该的线程都会复制到自己的本地,所以线程操作的都是本地的副本,这也就是说每个线程都是操作的自己本地的变量,那就完美的避免了线程安全的问题。

在这里还有一个问题。我在写这篇文章的时候看过很多文章,总的来说就是ThreadLocal就是为了解决多线程并发问题而提供的一种方法,还有一种解释就是ThreadLocal的最终目的就是为了解决多线程访问共享资源所产生的。真的对吗?ThreadLocal并没有共享那么从何而来的同步呢?

自己的想法

在看了Java并发编程之美后我所理解的Threadlocal提供了线程本地变量的副本,每个线程实际操作的是自己本地的变量副本,也就是说该变量副本只能当前线程访问,就不存在多个线程共享的问题,从Threadlocal名字我们也能看出本地线程。那那那它也就不存在去解决并发问题了。

如何使用

我们来看下面的例子。

ThreadLocal可以解决并发问题吗

输出结果:

Thread[Thread-1,5,main]====57
Thread[Thread-0,5,main]====75

创建了两个线程,它们都在threadlocal上面都set了一个随机数,我们看最后的输出结果每个都是不同的值,那么我们如果把threadlocal替换成一个集合会发生什么,由于两个线程时上个线程生成的随机数57会被第二个线程覆盖掉,而在Threadlocal中两个线程都是操作的自己的本地副本,那么两个线程互不影响都无法操控到对方的数据,因此它们存取的都是不同的值。

实现原理

那么Threadlocal是如何实现的呢?在研究Threadlocal的实现原理我们先看一下Thread的内部属性。

ThreadLocal可以解决并发问题吗

  • threadLocals 此线程保存的Threadlocal的值

inheritableThreadLocals等到后面再说。

Thread的内部属性中我们看到了这两个默认为null的属性,threadLocals用来保存Threadlocal的本地副本,默认是为null只有调用Threadlocal的set时才会创建。也就是说Threadlocal就类似一个工具,它的作用就是把value的值通过set存在线程每个线程的threadLocals 中,只要线程一直存在threadLocals 也就一直存在。所以当不需要使用本地变量的时候可以调用Threadlocal的remove来清空本地变量。而threadLocals 为什么继承于ThreadLocalMap呢?ThreadLocalMap是一个定制的HashMap,而使用Map的原因就是可以每个线程关联多个Threadlocal变量。

set方法

我们来看一下set方法是如何实现的。

ThreadLocal可以解决并发问题吗

可以看出流程非常简单,首先获取当前线程然后在进行下一步操作,我们在看一下getMap做了什么

ThreadLocal可以解决并发问题吗

getMap主要就是返回了当前threadLocals的属性。那如果map为空呢?

ThreadLocal可以解决并发问题吗

如果map为空的话就直接创建一个新的ThreadLocalMap。

我们来看一下流程图。

ThreadLocal可以解决并发问题吗

get方法

看一下Get方法

ThreadLocal可以解决并发问题吗

首先根据当前线程获取实例如果存在就返回,如果不存在就先初始化一个空值,然后判断如果当前threadLoacals不为空就直接set一个空,否则就创建一个变量。

remove方法

ThreadLocal可以解决并发问题吗

remove方法相对来说比较简单。

总结

Threadlocal的实现原理其实就是通过set把value set到线程的threadlocals属性中,threadlocals类型是Map其中的Key就是Threadlocal的this引用,value就是我们所set的值,如果当前线程不销毁的话threadlocals会一直存在。一直存在的话可能会造成内存溢出,所以使用完之后尽量remove一下。不过在这里又有一个问题那就是如果我的线程想要读取主线程的变量要怎么做?我们上面的例子都是设置的新创建的线程,那么现在我在主线程中set一个值,这个时候我在新创建的线程中可以读取到吗?答案是不可以,因为Threadlocal不支持继承性。

我们看下面的例子:

ThreadLocal可以解决并发问题吗

输出结果:

Thread[Thread-0,5,main]====null

也就是说Threadlocal不支持继承性,主线程设置了值,在子线程中是获取不到的。那我现在想要获取主线程里面的值要怎么做?

Threadlocal是实现不了的,不过Threadlocal有一个子类可以实现。InheritableThreadLocalInheritableThreadLocalThreadlocal的实现,我们来看一个简单的例子。

ThreadLocal可以解决并发问题吗

输出结果:

Thread[Thread-0,5,main]====1000

运行结果发现子线程是可以获取到主线程设置的值的,那它是如何实现的?

我们看一下代码实现:

ThreadLocal可以解决并发问题吗

InheritableThreadLocal是继承Threadlocal的,并且把threadlocals给替换成inheritableThreadLocals了所以上面的inheritableThreadLocals我要留在最后说,那么替换成inheritableThreadLocals后子线程就可以获取到主线程设置的属性了吗?我们在看一下Thread的实现。

ThreadLocal可以解决并发问题吗

Thread的初始化方法可以看出,先获取了当前线程(主线程)判断主线程的inheritableThreadLocals不为空的话就调用createInheritedMap方法赋值给子线程中的inheritableThreadLocals。具体不在这里解释太多。有机会在写一篇文章来解释。

本文完

ThreadLocal可以解决并发问题吗

SpringBoot优雅地配置日志

Java内存模型详解(一)

如何使用Arrays工具类操作数组

ThreadLocal可以解决并发问题吗

本文分享自微信公众号 - 乱敲代码(lqcoder)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal原理解析
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Easter79 Easter79
3年前
ThreadLocal设计模式
ThreadLocal设计模式使用的也很频繁,会经常在各大框架找到它们的踪影,如struts2以及最近正在看的SpringAOP等。ThreadLocal设计模式也有很多误解,我的理解是(1)ThreadLocal所操作的数据是线程间不共享的。它不是用来解决多个线程竞争同一资源的多线程问题。(2)ThreadLocal所操作的数据主要
Easter79 Easter79
3年前
ThreadLocal详解
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。  这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
京东云开发者 京东云开发者
2星期前
加锁失效,非锁之过,加之错也
作者:京东零售邢成引言多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题。银行两操作员同时操作同一账户就是典型的例子。比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去50元,A先提交
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k