多线程中ThreadLocal的使用

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
SimpleExecutor

前言

多线程是Java的一个重要特性,多线程从某方面可以等价于多任务,当你有多个任务要处理时,多个任务一起做所消耗的时间肯定比任务串行起来做,所消耗的时间短。而对于多线程不熟悉的新手则容易踩到很多坑,最典型的则是变量问题。

概念介绍

下面先用简单粗俗的语言解释一下几个基本概念

线程安全:多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。典型的例子为StringBuffer类。

线程不安全:不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。典型的例子为StringBuilder类。Servlet和SpringMVC采用的是单例设计模式,因此也是线程不安全的。而aop中如果定义了成员变量,也是线程不安全的。

Java内存模型

参考我之前的文章Java内存模型介绍

结合Java内存模型的介绍可知,在单例模式下,多个线程操作同一个变量,会发生线程安全性问题简单来说就是一个变量name在线程A中命名为“李铁蛋”,而线程B将其命名为“李蛋”,此时线程A输出变量name,极有可能输出的是“李蛋”。因此,就需要使用ThreadLocal来给每个线程提供局部变量,解决线程安全问题。

示例

首先我们来看一下关于线程不安全的情况

public class test003 implements Runnable {private Res res;public test003(Res res) {this.res = res;}public void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "," + res.getNumber());try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Res res = new Res();for (int i = 0; i < 4; i++) {new Thread(new test003(res)).start();}}}class Res {public Integer count = 0;public Integer getNumber() {return ++count;}}

程序中的res变量则为主内存中的变量,每个线程都会操作同一个res,获取到的也是同一个count,因此其中一次运行打印出来的结果如下

Thread-0,1
Thread-1,2
Thread-2,3
Thread-3,4
Thread-0,5
Thread-1,6
Thread-2,7
Thread-3,8
Thread-0,9
Thread-1,10
Thread-2,11
。。。。。

程序的本意为打印每个线程从1开始增长,而运行结果中,比如线程0,第一次为1,第二次为5,很明显不符合要求,我们将程序部分代码如下改造:

class Res {public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};// 这里其实可以使用JDK8的Lambda表达式简化代码// public static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);public Integer getNumber() {int count = threadLocal.get() + 1;threadLocal.set(count);return count;}}

改造后的代码,使用ThreadLocal创建一个成员变量,泛型为Integer,表示这个成员变量为int类型。在getNumber方法中,执行的则是count++操作。threadLocal.get()方法的作用是获取当前线程threadLocal中的值,+1之后获取本次的count,并set回去。我们看一下输出结果。

Thread-0,1
Thread-2,1
Thread-3,1
Thread-1,1
Thread-0,2
Thread-2,2
Thread-3,2
Thread-1,2
Thread-1,3
Thread-0,3
Thread-2,3
Thread-3,3
。。。。

每个线程的结果都是从1开始增长。

总结

ThreadLocal的作用是给每个线程提供局部变量,而这个局部变量就是存储到工作内存中的。线程之间的局部变量互不影响,达到线程安全的目的。ThreadLocal的应用相当广泛,如SpringCloud在网关中获取当前的request,就是使用的ThreadLocal

部分代码如下:

public class RequestContext extends ConcurrentHashMap<String, Object> {// ThreadLocal存储RequestContextprotected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {protected RequestContext initialValue() {try {return (RequestContext)RequestContext.contextClass.newInstance();} catch (Throwable var2) {throw new RuntimeException(var2);}}};// 获取Request上下文public static RequestContext getCurrentContext() {if (testContext != null) {return testContext;} else {RequestContext context = (RequestContext)threadLocal.get();return context;}}// 获取当前线程的requestpublic HttpServletRequest getRequest() {return (HttpServletRequest)this.get("request");}}

SpringCloud的源码我还没开始看(这玩意源码太多了估计啃不动),现在在啃Mybatis源码,因此下面对此的分析只是推测,还希望大佬们不要打我。

Zuul在请求进入后,首先会获取到request,并将其存储在RequestContext中,使用threadLocal存储,可以保证每个线程获取到的request都是属于自己的。后续在程序的任意处,都可以使用 RequestContext.getCurrentContext().getRequest() 来获取当前请求的request对象。

错误使用

在web应用中,经常会有人把ThreadLocal作为每个线程的全局变量使用,这种用法是错误的。SpringBoot底层有线程池,对于每一个请求,都会从线程池中随机取出一个线程,因此即使是同一个登录的用户,每一次请求都有可能不是同一个线程,而从ThreadLocal中获取到的值自然也不一样。关于每次请求都不是同一线程的问题,可以自行打印请求的线程id进行证明,这里就不贴代码了。

ThreadLocal在web应用中的使用场景为,为每次请求提供一个全局的值,在这一次请求中,可以在任何地方取出来这个值进行操作。如:在aop中解析token获取登录中的用户信息,存放到ThreadLocal,本次请求需要用到登录用户的信息,就可以取出来。再如:开发者在aop中记录日志,代码全部写到环绕通知中就显得冗余,因此获取ip、参数等内容会写到前置通知中。而对于要存表的日志,参数在前置通知,返回值在后置通知,报错信息在环绕通知中,可能会想到把变量定义到最上面,这种写法也是错误的。在上面说过,aop是单例模式,因此这种写法存在线程安全性问题,在这里就也可以使用ThreadLocal存储日志信息,最后在后置通知中存表。
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
SimpleExecutor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536944.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

注解版poi操作工具

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Kali Linux 2019.1 发布,Metasploit 更新到 5.0 版本

百度智能云 云生态狂欢季 热门云产品1折起>>> Kali Linux 2019.1 发布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一个基于 Debian 的 Linux 发行版&#xff0c;主要用于信息安全行业&#xff0c;其包含了一系列安全、渗透测试和取证工具。此版本 Linux 内核…

peewee mysql_scrapy中利用peewee插入Mysql

前两天老大布置一个任务&#xff0c;说爬下来的数据要存入数据库中&#xff0c;丢给我一个peewee&#xff0c;说用这个。当时的我两眼一抹黑&#xff0c;这是个什么东西呀&#xff0c;我知道scrapy的数据存入数据库是在pipelines中进行设置但是peewee是什么东西呢。经过两天不懈…

Java版数据结构与算法——线性表

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

基于 CODING 的 Spring Boot 持续集成项目

本文作者&#xff1a;CODING 用户 - 廖石荣 持续集成的概念 持续集成(Continuous integration,简称 CI&#xff09;是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;通常每个成员每天至少集成一次&#xff0c;也就意味着每天可能会发生多次集成。每…

Mybatis组成部分

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

一年java工作经验-面试总结

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

linux mysql python包_03_mysql-python模块, linux环境下python2,python3的

---恢复内容开始---1、Python2 正常[rootIP ~]#pip install mysql-pythonDEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 wont be maintained after that date. A future version of pip will drop …

两年Java工作经验应该会些什么技术

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

centos 6 mysql 5.7.13 编译安装_Centos 6.5 下面 源码编译 安装 Mysql 5.7.13

安装软件依赖包yum -y install gcc gcc-c ncurses ncurses-devel cmake下载软件包cd /usr/local/srcwget https://downloads.mysql.com/archives/get/file/mysql-5.7.13.tar.gz --no-check-certificate下载 boost 库&#xff0c;MySQL 5.7.5 开始Boost库是必需的cd /usr/loca…

LeetCode 237. 删除链表中的节点(Python3)

题目&#xff1a; 请编写一个函数&#xff0c;使其可以删除某个链表中给定的&#xff08;非末尾&#xff09;节点&#xff0c;你将只被给定要求被删除的节点。 现有一个链表 -- head [4,5,1,9]&#xff0c;它可以表示为: 示例 1: 输入: head [4,5,1,9], node 5 输出: [4,1,9…

一年Java经验应该会些什么

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

mysql查询各类课程的总学分_基于jsp+mysql的JSP学生选课信息管理系统

运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。IDE环境&#xff1a; Eclipse,Myeclipse,IDEA都可以硬件环境&#xff1a; windows 7/8/10 2G内存以上(推荐4G&#xff0c;4G以上更好)可以实现&#xff1a; 学生&#xff0c;教师角色的…

三年java经验应该会什么?

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

python加法运算符可以用来连接字符串并生成新字符串_中国大学MOOCPython语言入门网课答案...

中国大学MOOCPython语言入门网课答案表达式int(40.5)的值为____________。表达式160.5的值为____________________。python程序只能使用源代码进行运行&#xff0c;不能打包成可执行文件。python语句list(range(1,10,3))执行结果为___________________。pip命令也支持扩展名为.…

全是满满的技术文档

*************************************话不多说-先上教程 ********************************** 完整躺赚教程(不需任何技术,照做就能赚钱):点击此处获取 提取码&#xff1a;6666 被动收入教程(需要一定的技术,会搭建服务器,会发布项目<教程里面会教你>):点击此处获取 提…

JavaScript面试的完美指南(开发者视角)

2019独角兽企业重金招聘Python工程师标准>>> 摘要&#xff1a; 面试季手册。 原文&#xff1a;javascript 面试的完美指南(开发者视角)作者&#xff1a;前端小智Fundebug经授权转载&#xff0c;版权归原作者所有。 为了说明 JS 面试的复杂性&#xff0c;首先&#x…

windows qt 不能debug_linux配置vlc-qt

vlc-qt 是基于vlc库&#xff0c;用于开发音频视频应用&#xff0c;性能优秀。vlc-qt/vlc-qt​github.com使用vlc-qt首先需要编译vlc-qt &#xff08;windows可以下载使用编译好的&#xff0c;但是只能用在release模式&#xff09;&#xff08;在windows系统中&#xff09;使用w…

idou老师教你学Istio 27:解读Mixer Report流程

1、概述 Mixer是Istio的核心组件&#xff0c;提供了遥测数据收集的功能&#xff0c;能够实时采集服务的请求状态等信息&#xff0c;以达到监控服务状态目的。 1.1 核心功能 •前置检查&#xff08;Check&#xff09;&#xff1a;某服务接收并响应外部请求前&#xff0c;先通过E…

mysql删除密码代码_mysql 用户新建、受权、删除、密码修改

mysql 用户新建、授权、删除、密码修改首先要声明一下&#xff1a;一般情况下&#xff0c;修改MySQL密码&#xff0c;授权&#xff0c;是需要有mysql里的root权限的。注&#xff1a;本操作是在WIN命令提示符下&#xff0c;phpMyAdmin同样适用。用户&#xff1a;phplamp 用户数…