<强>介绍强>
ThreadLocal作为JDK1.2以来的一个java。朗包下的一个类,在面试和工程中都非常重要,这个类的主要目的是提供线程本地的变量,所以也有很多地方把这个类叫做线程本地变量
从字面理解,这个类为每个线程都创建了一个本地变量,实际上是ThreadLocal为变量在每个线程中都创建了一个副本,使得每个线程都可以访问自己内部的副本变量
通常提到多线程,都会考虑变量同步的问题,但是ThreadLocal并不是为了解决多线程共享变量同步的问题,而是为了让每个线程的变量不互相影响,相当于线程之间操纵的都是变量的副本,自然就不用考虑多线程竞争的问题,也自然没有性能损耗
<强>使用方式
强>
先来看常用的这几个方法
公共T get () {} 公共空集(T值){} 公共空间remove () {} 保护T initialValue () {}
显而易见,get()方法获取线程拥有的副本值,设置()方法进行设值,删除()方法移除,initialValue()进行变量初始化,我们先来看下面这个实例,同时体会一下应用场景
公开课演示{ 公共静态ThreadLocalthreadLocal=零; 公共静态void main (String [] args) { threadLocal=new ThreadLocal () {/* * *通过重写该方法来初始化ThreadLocal的值 */@Override 保护整数initialValue () { 返回10; } }; MyThread t1=new MyThread (20); MyThread t2=new MyThread (30); t1.start ();//这里为了描述清晰,省略了try - catch语句块 t1.join (); t2.start (); } }
在上述方法中,我们定义并初始化一个ThreadLocal类为10(通过重写initialValue()方法实现),然后开启了两个线程,同时我们这里让t2线程等待t1线程执行完再执行
MyThread类详细信息如下
类MyThread扩展线程{ 私人int val=0; MyThread (int val) { 这一点。val=val; } @Override 公共空间run () { System.out.println (Thread.currentThread() +”然后就——“+ Demo.threadLocal.get ()); Demo.threadLocal.set (val); System.out.println (Thread.currentThread () +”————“+ Demo.threadLocal.get ()); } }
我们通过调用ThreadLocal对象的获得()方法来获取当前的值,然后通过设置()方法设置一个新值(每个线程我们设置不同的值),然后再通过得到()方法来获取设置后的值
运行结果如下
重点是图中标注的t2线程变量的初始值,虽然我们在t1线程中修改了变量的值,但是在t2线程中变量值并没有被改变,这样就实现了每个线程独有的变量
同时,如果一个ThreadLocal对象要在很多地方进行复用时,需要在使用前通过调用* *删除()* *方法来将本地变量恢复到默认值
也许有人会问了,我们给每个线程定义自己的私有变量不是也可以实现同样的操作吗,理论上当然是可行的,但是ThreadLocal远比私有变量的形式方便,不仅可以在线程外部进行统一的初始化,而且避免在线程内部额外设置变量
<强>原理强>
点进ThreadLocal的源码中,发现并没有存储变量的字段值,那看来ThreadLocal并不负责保存变量,我们只能从方法下手
先看初始()方法,毕竟我们的变量默认初始值就是在这个方法中设置,如下
保护T initialValue () { 返回null; }
我们在每次创建ThreadLocal都要重写这个方法,那么这个方法到底在哪调用呢,我们点进得到()方法源码中,如下
公共T get () {//获取当前线程 线程t=Thread.currentThread ();//获取当前线程的地图 ThreadLocalMap地图=getMap (t); 如果(地图!=null) { ThreadLocalMap。输入e=map.getEntry(这个); 如果(e !=null) {=(T) e.value T结果; 返回结果; } }//如果地图为空则进行创建 返回setInitialValue (); }
有点眉头了,我们发现这里获取了一个ThreadLocalMap对象,所以会想到有可能是通过让线程与变量作一个千伏表,来实现每个线程拥有自己独有的变量