前言

到底什么是线程的不安全?为什么会存在线程的不安全?线程的不安全其实就是多个线程并发的去操作同一共享变量没用做同步所产生意料之外的结果。那是如何体现出来的呢?我们看下面的一个非常经典的例子:两个操作员同时操作同一个银行账户,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有一个子类可以实现。InheritableThreadLocalInheritableThreadLocalThreadlocal的实现,我们来看一个简单的例子。

输出结果:

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

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

我们看一下代码实现:

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

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

本文完

ThreadLocal可以解决并发问题吗?的更多相关文章

  1. 【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】

    一.使用过滤器实现全站压缩 1.目标:对网站的所有JSP页面进行页面压缩,减少用户流量的使用.但是对图片和视频不进行压缩,因为图片和视频的压缩率很小,而且处理所需要的服务器资源很大. 2.实现原理: ...

  2. EntityFramework Core解决并发详解

    前言 对过年已经无感,不过还是有很多闲暇时间来学学东西,这一点是极好的,好了,本节我们来讲讲EntityFramewoek Core中的并发问题. 话题(EntityFramework Core并发) ...

  3. 使用mysql悲观锁解决并发问题

    最近学习了一下数据库的悲观锁和乐观锁,根据自己的理解和网上参考资料总结如下: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持 ...

  4. hibernate 解决并发问题

    hibernate 解决并发问题的策略有 1)设置hibernate事务隔离级别 2)hibernate中乐观锁的实现 ps:版本号是由hibernate自己维护的,我们自己只需要做以上二步即可实现乐 ...

  5. list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题,for可以解决并发问题

    list的迭代器能解决并发问题,collection 的迭代器不能解决并发问题 为什么list支持add,collection不支持 例如有两个人同时添加第三个元素 list的迭代器能锁定线程 只有等 ...

  6. PHP解决并发问题的几种实现

    对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了 在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发控制. 方案一:使用文件锁排它锁 f ...

  7. python单线程解决并发

    1.单线程解决并发 方式一 import socket import select # 百度创建连接:非阻塞 client1 = socket.socket() client1.setblocking ...

  8. C#解决并发的设计思路

    解决并发的方案,应用场景,一个报名的方法,可是要限制报名的人数:一,如果是单机版,就是部署一个服务器站点的我们可以使用很经典的lock锁,或者queue队列,针对单机版二,如果是部署了集群的站点1&g ...

  9. 【漫画】CAS原理分析!无锁原子类也能解决并发问题!

    本文来源于微信公众号[胖滚猪学编程].转载请注明出处 在漫画并发编程系统博文中,我们讲了N篇关于锁的知识,确实,锁是解决并发问题的万能钥匙,可是并发问题只有锁能解决吗?今天要出场一个大BOSS:CAS ...

随机推荐

  1. 通过VS2017发布.net core程序并使用Web 部署到远程服务器最新教程

    最近一个项目中,为App开发后台接口,技术选型为最新 .net core版本,使用.net core开发web api接口过程中,为了方便app团队成员直接在线调用接口,找了公网上的一台服务器做为ap ...

  2. PHP中的$_POST变量

    定义 在 PHP 中,预定义的 $_POST 变量用于收集来自 method="post" 的表单中的值. $_POST 变量 预定义的 $_POST 变量用于收集来自method ...

  3. python查漏补缺 --- 模块及异常

    1.方法定义好之后,如,def test(x) : ,此时将方法名赋值给一个新的变量,那么该变量等同于方法,可以具备test方法内部的全部功能2.导包的时候,可以使用as关键字在不同的名称下导入模块或 ...

  4. 使用log4j进行日志管理

      17.1.Log4j简介 作用: 1. 跟踪代码的运行轨迹. 2. 输出调试信息. 三大组成: 1. Logger类-生成日志. 2. Appender类-定义日志输出的目的地. 3. Layou ...

  5. 2019全国大学生信息安全与对抗技术竞赛全国线下总决赛 Writeup

    0x00 Begin 关于 ISCC 2019 北理工总决赛,这一次比赛体验感总体差不多,最后我们战队荣获全国一等奖第一名,在这里非常感谢我的团队以及我的队友. 0x01 Reverse 下载题目:e ...

  6. Promise异步编程解决方案

    Promise是ES6中新增的异步编程解决方案,体现在代码中它是一个对象,可以通过 Promise 构造函数来实例化. 其最基本的使用 new Promise(function(resolve,rej ...

  7. Linux基本操作及安装(部分)

    1.分别用cat \tac\nl三个命令查看文件/etc/ssh/sshd_config文件中的内容,   并用自己的话总计出这三个文档操作命令的不同之处? [root@localhost ~]# c ...

  8. 【JDK】JDK源码分析-Collection

    Java 集合框架(Java Collections Framework, JCF)包含很多平时开发中的常用类,例如 List.Set.ArrayList.HashMap.HashSet 等,因此打算 ...

  9. golang文档、中文、学习文档

    Golang中文文档地址 http://zh-golang.appspot.com/doc/ Golang非英文文档地址: https://github.com/golang/go/wiki/NonE ...

  10. 【NOI 2015】程序自动分析 并查集与离散化处理

    题目描述 在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足. 考虑一个约束满足问题的简化版本:假设x1,x2,x3,-代表程序中出现的变量,给定n个形如xi=xj或xi≠xj的变量 ...