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

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

在添加同步锁后我们可以看到,A操作员和B操作员同时去操作账户,但是A先抢占到资源,所以B就只能等待A操作员释放锁才能去操作银行账户,那么最终结果是我们所预期的吗?答案是的。
同步的话一般都是加锁,如果现在我想创建多个线程每个线程都是访问的自己的变量呢?各个线程之间毫无关联?
答案是有的。
ThreadLocal问题
ThreadLocal是JDK提供的,它提供了线程本地变量。什么是线程本地变量呢?其实就是你创建了一个Threadlocal变量,每个访问Threadlocal变量的线程都有一个本地副本。我们看下面的图:
从上面看出你创建一个ThreadLocal变量,每个访问该的线程都会复制到自己的本地,所以线程操作的都是本地的副本,这也就是说每个线程都是操作的自己本地的变量,那就完美的避免了线程安全的问题。
在这里还有一个问题。我在写这篇文章的时候看过很多文章,总的来说就是ThreadLocal就是为了解决多线程并发问题而提供的一种方法,还有一种解释就是ThreadLocal的最终目的就是为了解决多线程访问共享资源所产生的。真的对吗?ThreadLocal并没有共享那么从何而来的同步呢?
自己的想法
在看了Java并发编程之美后我所理解的Threadlocal提供了线程本地变量的副本,每个线程实际操作的时自己本地的变量副本,也就是说该变量副本只能当前线程访问,就不存在多个线程共享的问题,从Threadlocal名字我们也能看出本地线程。那那那它也就不存在去解决并发问题了。
如何使用
我们来看下面的例子。

输出结果:
Thread[Thread-1,5,main]====57
Thread[Thread-0,5,main]====75
创建了两个线程,它们都在threadlocal上面都set了一个随机数,我们看最后得输出结果每个都是不同得值,那么我们如果把threadlocal替换成一个集合会发生什么,由于两个线程时上个线程生成的随机数57会被第二个线程覆盖掉,而在Threadlocal中两个线程都是操作的自己的本地副本,那么两个线程互不影响都无法操控到对方的数据,因此它们存取的都是不同的值。
实现原理
那么Threadlocal是如何实现的呢?在研究Threadlocal的实现原理我们先看一下Thread的内部属性。

- 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方法是如何实现的。

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

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

如果map为空的话就直接创建一个新的ThreadLocalMap。
我们来看一下流程图。

get方法
看一下Get方法

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

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

输出结果:
Thread[Thread-0,5,main]====null
也就是说Threadlocal不支持继承性,主线程设置了值,在子线程中是获取不到的。那我现在想要获取主线程里面的值要怎么做?
Threadlocal是实现不了的,不过Threadlocal有一个子类可以实现。InheritableThreadLocal,InheritableThreadLocal是Threadlocal的实现,我们来看一个简单的例子。

输出结果:
Thread[Thread-0,5,main]====1000
运行结果发现子线程是可以获取到主线程设置的值的,那它是如何实现的?
我们看一下代码实现:

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

看Thread的初始化方法可以看出,先获取了当前线程(主线程)判断主线程的inheritableThreadLocals不为空的话就调用createInheritedMap方法赋值给子线程中的inheritableThreadLocals。具体这里解释太多。有机会在写一篇文章来解释。
本文完
ThreadLocal可以解决并发问题吗?的更多相关文章
- 【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】
一.使用过滤器实现全站压缩 1.目标:对网站的所有JSP页面进行页面压缩,减少用户流量的使用.但是对图片和视频不进行压缩,因为图片和视频的压缩率很小,而且处理所需要的服务器资源很大. 2.实现原理: ...
- EntityFramework Core解决并发详解
前言 对过年已经无感,不过还是有很多闲暇时间来学学东西,这一点是极好的,好了,本节我们来讲讲EntityFramewoek Core中的并发问题. 话题(EntityFramework Core并发) ...
- 使用mysql悲观锁解决并发问题
最近学习了一下数据库的悲观锁和乐观锁,根据自己的理解和网上参考资料总结如下: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持 ...
- hibernate 解决并发问题
hibernate 解决并发问题的策略有 1)设置hibernate事务隔离级别 2)hibernate中乐观锁的实现 ps:版本号是由hibernate自己维护的,我们自己只需要做以上二步即可实现乐 ...
- list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题,for可以解决并发问题
list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题 为什么list支持add,collection不支持 例如有两个人同时添加第三个元素 list的迭代器能锁定线程 只有等 ...
- PHP解决并发问题的几种实现
对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了 在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发控制. 方案一:使用文件锁排它锁 f ...
- python单线程解决并发
1.单线程解决并发 方式一 import socket import select # 百度创建连接:非阻塞 client1 = socket.socket() client1.setblocking ...
- C#解决并发的设计思路
解决并发的方案,应用场景,一个报名的方法,可是要限制报名的人数:一,如果是单机版,就是部署一个服务器站点的我们可以使用很经典的lock锁,或者queue队列,针对单机版二,如果是部署了集群的站点1&g ...
- 【漫画】CAS原理分析!无锁原子类也能解决并发问题!
本文来源于微信公众号[胖滚猪学编程].转载请注明出处 在漫画并发编程系统博文中,我们讲了N篇关于锁的知识,确实,锁是解决并发问题的万能钥匙,可是并发问题只有锁能解决吗?今天要出场一个大BOSS:CAS ...
随机推荐
- linux初学者-系统服务的控制
linux系统中系统服务的控制是比较重要的一部分,这也直接影响到计算机的使用,以下将会介绍一些系统服务的控制. 1.系统服务命令 系统的初始化程序是系统开始的第一个进程,pid为1.可以通过以下命 ...
- python load,loads,dumps,dump区别
json 模块提供了一种很简单的方式来编码和解码JSON数据. 其中两个主要的函数是 json.dumps()和 json.loads() , 要比其他序列化函数库如pickle的接口少得多. 下面演 ...
- storm入门demo
一.storm入门demo的介绍 storm的入门helloworld有2种方式,一种是本地的,另一种是远程. 本地实现: 本地写好demo之后,不用搭建storm集群,下载storm的相关jar包即 ...
- Centos7配置BIND开机自启动
Centos7上面已经把/etc/init.d/服务的启动方式更改为systemctl启动. 当然编译安装仍然可以/etc/init.d/手动启动但是chkconfig –add named就用不了. ...
- React躬行记(11)——Redux基础
Redux是一个可预测的状态容器,不但融合了函数式编程思想,还严格遵循了单向数据流的理念.Redux继承了Flux的架构思想,并在此基础上进行了精简.优化和扩展,力求用最少的API完成最主要的功能,它 ...
- TestNG独立运行的几种方法.md
目录 通过main函数调用testng.xml文件,然后打成可执行jar包执行 1 假设我们已经写好了testng.xml,现在我们需要写一个主类和main函数用来调用testng.xml 2 把项目 ...
- codeforces 340 A. The Wall
水水的一道题,只需要找xy的最小公倍数,然后找a b区间有多少个可以被xy的最小公倍数整除的数,就是答案. //============================================ ...
- Android | Sqlite3
Android 数据库创建及使用: 创建: package he3.sd.dao; import android.content.Context; import android.database.sq ...
- SpringBoot第一天
一,SpringBoot 介绍 1,如果使用 Spring 开发一个"HelloWorld"的 web 应用: • 创建一个 web 项目并且导入相关 jar 包.SpringMV ...
- HTML/CSS:图片居中(水平居中和垂直居中)
css图片居中(水平居中和垂直居中) css图片居中分css图片水平居中和垂直居中两种情况,有时候还需要图片同时水平垂直居中, 下面分几种居中情况分别介绍: css图片水平居中 1.利用margin: ...