学习ThreadLocal的时候,不清楚为什么要用它。网上一堆乱七八糟的,看了许多没一个切中要害。自己总结了一下。
提示:本文需要一定的jvm内存模型知识
①、是什么?
ThreadLocal是一个用来提供 线程私有变量 的类
(重点)②、为什么用它?
为了提供 “全局”“线程私有“的变量。
下面详细展开说:
首先,全局很好用,但是是线程共享的。
全局是经常用到的概念,在程序的任何地方,都可以简单的通过 类名.方法名/域名 的方式来调用,提供了很大的便利性。
全局变量显然无法通过对象实例获得,而是需要通过类来获得。而类的成员分配在在方法区,方法区是线程共享的。也就是说,通过类获取的静态成员,是线程共享的。
然后,线程私有,它不是全局的。
线程私有也是常用到的概念。同一个函数,在不同线程执行,由于其变量(基本类型、引用类型)都是在栈上分配,而栈是线程私有的,所以变量也是线程私有的。显然你不可以像使用类名.xxx那样,全局使用一个基本类型、引用类型。
所以,目前的情况是,线程私有和全局无法兼得。如果你想让使用一个线程独立的全局变量,那是很难的。所以就出来一个ThreadLocal,它给你打包好了这个功能!
③、怎么用它?
将Thread local封装成一个类的静态成员,然后就可以获得一个“全局 线程私有”的变量了。
(如果没有全局需求,不如直接在栈上搞一个出来。。)
实例:
封装成一个类的静态成员
通过类名,即可实现全局线程私有调用:
结果:
④、什么时候用它?
要求线程私有时,如果有“全局”需求,那么就可用。
如果没有全局需求,直接使用普通的局部变量,就完全能够满足。
常见两种:
①线程不安全的工具类。
工具类经常在全局各个地方用,虽然每次用的时候new新的出来可以解决线程不安全问题,但是性能损耗大。
全局需求:避免频繁创建对象,减少内存消耗
例如:SimpleDateFormat是线程不安全的,将其封装到一个类的ThreadLocal中,即可实现线程安全+少内存消耗。
②线程私有的全局上下文数据。
上下文数据虽然可以通过一层一层通过函数参数传递,实现线程私有,但是过于繁琐。
全局需求:通过“全局”上下文的便利性,简化代码。
例如:服务器每次接收请求,都会新建一个线程来处理。对于当前用户信息的处理,如果按传统方法,那么就要通过一层一层的函数参数传递下去:Controller传给Service又传给DAO(这是简单的模型,实际业务经常更复杂)。此时就可以用ThreadLoacl包装,使用时可以简单的通过类名.xxx.get()来获得,省略了繁琐的传参,还保证了线程私有。
实际上,spring对Request的处理,就是使用的这种方式。