ThreadLocal就是这么简单

前言

今天要研究的是ThreadLocal,这个我在一年前学习JavaWeb基础的时候接触过一次,当时在baidu搜出来的第一篇博文ThreadLocal,在评论下很多开发者认为那博主理解错误,给出了很多有关的链接来指正(可原博主可能没上博客了,一直没做修改)。我也去学习了一番,可惜的是当时还没有记录的习惯,直到现在仅仅记住了一些当时学过的皮毛。

因此,做一些技术的记录是很重要的~同时,ThreadLocal也是面试非常常见的面试题,对Java开发者而言也是一个必要掌握的知识点~

当然了,如果我有写错的地方请大家多多包涵,欢迎在评论下留言指正~

一、什么是ThreadLocal

声明:本文使用的是JDK 1.8

首先我们来看一下JDK的文档介绍:


/*** This class provides thread-local variables.  These variables differ from* their normal counterparts in that each thread that accesses one (via its* {@code get} or {@code set} method) has its own, independently initialized* copy of the variable.  {@code ThreadLocal} instances are typically private* static fields in classes that wish to associate state with a thread (e.g.,* a user ID or Transaction ID).* * <p>For example, the class below generates unique identifiers local to each* thread.* A thread's id is assigned the first time it invokes {@code ThreadId.get()}* and remains unchanged on subsequent calls.*/     

结合我的总结可以这样理解:ThreadLocal提供了线程的局部变量,每个线程都可以通过set()get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离~。

简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。

二、为什么要学习ThreadLocal?

从上面可以得出:ThreadLocal可以让我们拥有当前线程的变量,那这个作用有什么用呢???

2.1管理Connection

最典型的是管理数据库的Connection:当时在学JDBC的时候,为了方便操作写了一个简单数据库连接池,需要数据库连接池的理由也很简单,频繁创建和关闭Connection是一件非常耗费资源的操作,因此需要创建数据库连接池~

那么,数据库连接池的连接怎么管理呢??我们交由ThreadLocal来进行管理。为什么交给它来管理呢??ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!

当时候写的代码:


public class DBUtil {//数据库连接池private static BasicDataSource source;//为不同的线程管理连接private static ThreadLocal<Connection> local;static {try {//加载配置文件Properties properties = new Properties();//获取读取流InputStream stream = DBUtil.class.getClassLoader().getResourceAsStream("连接池/config.properties");//从配置文件中读取数据properties.load(stream);//关闭流stream.close();//初始化连接池source = new BasicDataSource();//设置驱动source.setDriverClassName(properties.getProperty("driver"));//设置urlsource.setUrl(properties.getProperty("url"));//设置用户名source.setUsername(properties.getProperty("user"));//设置密码source.setPassword(properties.getProperty("pwd"));//设置初始连接数量source.setInitialSize(Integer.parseInt(properties.getProperty("initsize")));//设置最大的连接数量source.setMaxActive(Integer.parseInt(properties.getProperty("maxactive")));//设置最长的等待时间source.setMaxWait(Integer.parseInt(properties.getProperty("maxwait")));//设置最小空闲数source.setMinIdle(Integer.parseInt(properties.getProperty("minidle")));//初始化线程本地local = new ThreadLocal<>();} catch (IOException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {if(local.get()!=null){return local.get();}else{//获取Connection对象Connection connection = source.getConnection();//把Connection放进ThreadLocal里面local.set(connection);//返回Connection对象return connection;}}//关闭数据库连接public static void closeConnection() {//从线程中拿到Connection对象Connection connection = local.get();try {if (connection != null) {//恢复连接为自动提交connection.setAutoCommit(true);//这里不是真的把连接关了,只是将该连接归还给连接池connection.close();//既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了local.remove();}} catch (SQLException e) {e.printStackTrace();}}}

同样的,Hibernate对Connection的管理也是采用了相同的手法(使用ThreadLocal,当然了Hibernate的实现是更强大的)~

2.2避免一些参数传递

避免一些参数的传递的理解可以参考一下Cookie和Session:

  • 每当我访问一个页面的时候,浏览器都会帮我们从硬盘中找到对应的Cookie发送过去。
  • 浏览器是十分聪明的,不会发送别的网站的Cookie过去,只带当前网站发布过来的Cookie过去

浏览器就相当于我们的ThreadLocal,它仅仅会发送我们当前浏览器存在的Cookie(ThreadLocal的局部变量),不同的浏览器对Cookie是隔离的(Chrome,Opera,IE的Cookie是隔离的【在Chrome登陆了,在IE你也得重新登陆】),同样地:线程之间ThreadLocal变量也是隔离的....

那上面避免了参数的传递了吗??其实是避免了。Cookie并不是我们手动传递过去的,并不需要写<input name= cookie/>来进行传递参数...

在编写程序中也是一样的:日常中我们要去办理业务可能会有很多地方用到身份证,各类证件,每次我们都要掏出来很麻烦

// 咨询时要用身份证,学生证,房产证等等....public void consult(IdCard idCard,StudentCard studentCard,HourseCard hourseCard){}// 办理时还要用身份证,学生证,房产证等等....public void manage(IdCard idCard,StudentCard studentCard,HourseCard hourseCard) {}//......

而如果用了ThreadLocal的话,ThreadLocal就相当于一个机构,ThreadLocal机构做了记录你有那么多张证件。用到的时候就不用自己掏了,问机构拿就可以了。

在咨询时的时候就告诉机构:来,把我的身份证、房产证、学生证通通给他。在办理时又告诉机构:来,把我的身份证、房产证、学生证通通给他。...

// 咨询时要用身份证,学生证,房产证等等....public void consult(){threadLocal.get();}// 办理时还要用身份证,学生证,房产证等等....public void takePlane() {threadLocal.get();}

这样是不是比自己掏方便多了。

当然了,ThreadLocal可能还会有其他更好的作用,如果知道的同学可在评论留言哦~~~

三、ThreadLocal实现的原理

想要更好地去理解ThreadLocal,那就得翻翻它是怎么实现的了~~~

声明:本文使用的是JDK 1.8

首先,我们来看一下ThreadLocal的set()方法,因为我们一般使用都是new完对象,就往里边set对象了

public void set(T value) {// 得到当前线程对象Thread t = Thread.currentThread();// 这里获取ThreadLocalMapThreadLocalMap map = getMap(t);// 如果map存在,则将ThreadLocal作为key,要存储的对象作为value存到map里面去if (map != null)map.set(this, value);elsecreateMap(t, value);}

上面有个ThreadLocalMap,我们去看看这是什么?


static class ThreadLocalMap {/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal object).  Note that null keys (i.e. entry.get()* == null) mean that the key is no longer referenced, so the* entry can be expunged from table.  Such entries are referred to* as "stale entries" in the code that follows.*/static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}//....很长
}

通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储

我们的值都是存储到这个Map上的,key是当前ThreadLocal对象

如果该Map不存在,则初始化一个:

void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}

如果该Map存在,则从Thread中获取

/*** Get the map associated with a ThreadLocal. Overridden in* InheritableThreadLocal.** @param  t the current thread* @return the map*/ThreadLocalMap getMap(Thread t) {return t.threadLocals;}

Thread维护了ThreadLocalMap变量

/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null

从上面又可以看出,ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中

于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象

有了上面的基础,我们看get()方法就一点都不难理解了:

public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}

3.1ThreadLocal原理总结

  1. 每个Thread维护着一个ThreadLocalMap的引用
  2. ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  3. 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  4. 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
  5. ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value

正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~

四、避免内存泄露

我们来看一下ThreadLocal的对象关系引用图:

img_8675b2694b5a4b386b78d7aa58ef6652.jpe

ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用

想要避免内存泄露就要手动remove()掉

五、总结

ThreadLocal这方面的博文真的是数不胜数,随便一搜就很多很多~站在前人的肩膀上总结了这篇博文~

最后要记住的是:ThreadLocal设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题

如果看得不够过瘾,觉得不够深入的同学可参考下面的链接,很多的博主还开展了一些扩展知识,我就不一一展开了~

参考博文:

  • http://blog.xiaohansong.com/2016/08/06/ThreadLocal-memory-leak/
  • https://www.cnblogs.com/zhangjk1993/archive/2017/03/29/6641745.html#_label2
  • http://www.cnblogs.com/dolphin0520/p/3920407.html
  • http://www.cnblogs.com/dolphin0520/p/3920407.html
  • http://www.iteye.com/topic/103804
  • https://www.cnblogs.com/xzwblog/p/7227509.html
  • https://blog.csdn.net/u012834750/article/details/71646700
  • https://blog.csdn.net/winwill2012/article/details/71625570
  • https://juejin.im/post/5a64a581f265da3e3b7aa02d

img_b9281e41ca8ec4a8c7abeb39bd7a8fef.jpe

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y

更多的文章可往:文章的目录导航

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

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

相关文章

如何在Twitch上设置捐款

Many people on Twitch stream as a hobby. If you’re thinking about going full-time, though, you’ll need to raise some cash. Setting up donations on Twitch is one way you can do it! Twitch上的许多人都将其作为爱好。 但是&#xff0c;如果您打算全职工作&#x…

JAVA-Concurrency之CountDownLatch说明

2019独角兽企业重金招聘Python工程师标准>>> As per java docs, CountDownLatch is a synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. CountDownLatch concept is very comm…

设置微软应用商店的代理_如何设置和使用Microsoft家庭安全应用

设置微软应用商店的代理The Microsoft Family Safety app provides a set of reporting and parental control tools for people with Microsoft accounts. With filtering controls, location reporting, and app-usage recording, this app gives parents a way to monitor t…

Linux跨平台远程控制

转载于:https://blog.51cto.com/13660858/2094987

Zoom Host可以真正看到您的所有私人消息吗?

Girts Ragelis/Shutterstock.comGirts Ragelis / Shutterstock.comViral social media posts are alleging that Zoom’s private messages aren’t really private—if you’re chatting privately during a Zoom meeting, the host can see your entire conversation. Is tha…

使用Keras进行深度学习:(三)使用text-CNN处理自然语言(上)

欢迎大家关注我们的网站和系列教程&#xff1a;http://www.tensorflownews.com/&#xff0c;学习更多的机器学习、深度学习的知识&#xff01; 上一篇文章中一直围绕着CNN处理图像数据进行讲解&#xff0c;而CNN除了处理图像数据之外&#xff0c;还适用于文本分类。CNN模型首次…

powerpoint转换器_如何将PowerPoint演示文稿转换为主题演讲

powerpoint转换器If someone sends you a Microsoft PowerPoint presentation, but you’d rather use Apple’s presentation software, Keynote, you’re in luck! Apple’s done all the hard work for you. Here’s how to convert a PowerPoint presentation to Keynote. …

Android高仿大众点评(带服务端)

2019独角兽企业重金招聘Python工程师标准>>> 实例讲解了一个类似大众点评的项目&#xff0c;项目包含服务端和android端源码, 服务端为php代码&#xff0c;如果没有接触过php, 文章中讲解一键部署php的方法&#xff0c;让您5分钟将服务端搭建成功, 您也可以将php换成…

vista任务栏透明_在Windows XP中获取Vista任务栏缩略图预览

vista任务栏透明It was only a matter of time before people started cloning Windows Vista features and adding them into Windows XP. One of my favorite Vista features is the thumbnails that popup when you mouse over the taskbar. And now I can use them in XP a…

Spring实战Day2

创建对象之后如何体现对象之间的依赖&#xff1f; Spring容器负责创建Bean和依赖注入&#xff0c;那么Spring是怎么将Bean装配在一起的呢&#xff1f; Spring提供了三种方式装配机制 1.隐式的bean发现机制和自动装配 图一图二&#xff0c;是两个组件与Config类同包 图三&#x…

Git的状态转换

近期公司用Git来管理代码&#xff0c;用起来是要比svn爽一些。就是刚接触的时候比較痛苦&#xff0c;特别是那些状态(版本号的提交/合并/回退)。差点把我搞晕了。如今回过头来总结一下&#xff0c;就清楚多了。 就本地仓库来看。Git能够分成5个不同的状态。能够通过$ git statu…

RN自定义组件封装 - 播放类似PPT动画

1. 前言 近日&#xff0c;被安排做一个开场动画的任务。虽然RN提供了Animated来自定义动画&#xff0c;但是本次动画中的元素颇多&#xff0c;交互甚烦。。。在完成任务的同时&#xff0c;发现很多步骤其实是重复的&#xff0c;于是封装了一个小组件记录一下&#xff0c;分享给…

dropbox mac_如何在Windows或Mac上启动时阻止Dropbox打开

dropbox macDropbox is a handy way to synchronize files across devices via the cloud. By default, Dropbox starts whenever you turn on your Windows PC or Mac, but sometimes you might not want it to. Here’s how to make sure it doesn’t launch when you startu…

深入分析 ThreadLocal 内存泄漏问题

2019独角兽企业重金招聘Python工程师标准>>> ThreadLocal 的作用是提供线程内的局部变量&#xff0c;这种变量在线程的生命周期内起作用&#xff0c;减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。但是如果滥用 ThreadLocal&#xff0c;就可能会…

如何将iPhone应用程序从应用程序库移动到主屏幕

Justin Duino贾斯汀杜伊诺(Justin Duino)So as to not clutter up your home screen, newly-downloaded apps from the App Store can be sent directly to the App Library. But what if you later want to open the app without digging through the library? Here’s how t…

luogu4389 付公主的背包

题目链接&#xff1a;洛谷 题目大意&#xff1a;现在有$n$个物品&#xff0c;每种物品体积为$v_i$&#xff0c;对任意$s\in [1,m]$&#xff0c;求背包恰好装$s$体积的方案数&#xff08;完全背包问题&#xff09;。 数据范围&#xff1a;$n,m\leq 10^5$ 这道题&#xff0c;看到…

Git与Github的连接与使用

2019独角兽企业重金招聘Python工程师标准>>> Git与Github的连接与使用 下面继续&#xff0c;使用git 将项目上传到GitHub上 首先要有GitHub账号,这就不用说了&#xff0c;没有的先注册&#xff0c;地址&#xff1a;https://github.com 没有仓库的话&#xff0c;先新…

ttl电路制作pong游戏_如何玩Mozilla Firefox的隐藏的独角兽Pong游戏

ttl电路制作pong游戏It seems like every browser has a hidden game these days. Chrome has a dinosaur game, Edge has surfing, and Firefox has . . . unicorn pong? Yep, you read that right—here’s how to play it. 这些天似乎每个浏览器都有一个隐藏的游戏。 Chrom…

为什么无法运行谷歌play_什么是Google Play积分,以及如何使用它们?

为什么无法运行谷歌playThe Google Play Store is home to thousands of apps, games, movies, e-books, and more. You might find yourself making a lot of purchases there, so why not get rewarded for it? That’s where Google Play Points come in. Google Play商店提…

2019年春季学期第三周作业

本周作业 本周请大家完成上周挑战作业的第一部分&#xff1a;给定一个整数数组(包含正负数)&#xff0c;找到一个具有最大和的子数组&#xff0c;返回其最大的子数组的和。 例如&#xff1a;[1, -2, 3, 10, -4, 7, 2, -5]的最大子数组为[3, 10, -4, 7, 2] 1&#xff09;.实验代…