那个网站做精防手机,扶风高端企业网站建设,怀化市建设局招投标网站,网站备案 论坛目录
一、ThreadLocal类基本介绍 1.概述 : 2.作用及特定 :
二、ThreadLocal类源码解读 1.代码准备 : 1.1 图示 1.2 数据对象 1.3 测试类 1.4 运行测试 2.源码分析 : 2.1 set方法解读 2.2 get方法解读 一、ThreadLocal类基本介绍 1.概述 : (1) ThreadLocal#xff0c;本…目录
一、ThreadLocal类基本介绍 1.概述 : 2.作用及特定 :
二、ThreadLocal类源码解读 1.代码准备 : 1.1 图示 1.2 数据对象 1.3 测试类 1.4 运行测试 2.源码分析 : 2.1 set方法解读 2.2 get方法解读 一、ThreadLocal类基本介绍 1.概述 : (1) ThreadLocal本地线程变量是Java中的一个类。ThreadLocal类提供了一种线程绑定机制可以将状态与线程Thread关联起来。ThreadLocal类如下图所示 : (2) 每个线程都会有自己独立的一个ThreadLocal变量该变量对其他线程而言是封闭且隔离的因此对该变量的读写操作只会影响到当前执行线程的这个变量而不会影响到其他线程的同名变量。 2.作用及特定 : (1) ThreadLocal可以实现在同一个线程数据共享从而解决多线程数据安全问题。 (2) 通过ThreadLocal类实例的set方法可以为当前线程关联一个数据(变量对象数组)。 (3) 通过ThreadLocal类实例的get方法可以像Map一样存取key-value键值对(其中key为当前线程)注意显式的用法上与Map不相同。 (4) 每一个ThreadLocal对象只能为当前线程关联一个数据若想为当前线程关联多个数据就需要使用到多个ThreadLocal实例。 (5) ThreadLocal实例往往定义为static类型。 (6) ThreadLocal中保存的数据会在线程销毁后自动释放。 二、ThreadLocal类源码解读 1.代码准备 : 1.1 图示 首先我们要把代码打通确保ThreadLocal对象可以在同一线程中实现数据共享。根据下图来定义所需要的测试类 : 在T1类T1Service类以及T2DAO类中分别打印出当前线程的名字以及放入到threadLocal1对象中的数据对象对比三个类打印出的线程名字和数据对象是否相同即可验证“ThreadLocal可以实现在同一个线程数据共享”。 1.2 数据对象 定义Apple类和Grape类用作测试的数据对象。 Apple类代码如下 :
package threadlocal;public class Apple {
} Grape类代码如下 :
package threadlocal;/*** author : Cyan_RA9* version : 21.0*/
public class Grape {
}1.3 测试类 T1类代码如下 :
package threadlocal;/*** author : Cyan_RA9* version : 21.0*/
public class T1 {//定义一个静态的ThreadLocal对象public static ThreadLocalObject threadLocal1 new ThreadLocal();public static void main(String[] args) {//在主线程中启动一个新的子线程new Thread(new Task()).start();}public static class Task implements Runnable{Overridepublic void run() {System.out.println((Task)run方法,当前线程名 Thread.currentThread().getName());Apple apple new Apple();Grape grape new Grape();//向threadLocal1对象中放入一个Apple对象System.out.println((Task)run方法,放入的对象 apple);threadLocal1.set(apple);new T1Service().test();}}
}T1Service类代码如下 :
package threadlocal;/*** author : Cyan_RA9* version : 21.0*/
public class T1Service {public void test() {String name Thread.currentThread().getName();System.out.println((T1Service)当前线程名 name);Object o T1.threadLocal1.get();System.out.println((T1Service)得到的对象o o);new T2DAO().test();}
}T2DAO类代码如下 :
package threadlocal;/*** author : Cyan_RA9* version : 21.0*/
public class T2DAO {public void test() {String name Thread.currentThread().getName();System.out.println((T2DAO)当前线程名 name);Object o T1.threadLocal1.get();System.out.println((T2DAO)得到的对象o o);}
}1.4 运行测试 运行结果 : 2.源码分析 : 2.1 set方法解读 set方法源码如下 : public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {map.set(this, value);} else {createMap(t, value);}} 第一步可以看到set方法中首先就获取到了当前线程而当前线程就是调用set方法时——线程类run方法所在的那个线程说明set方法和当前线程是关联的。 第二步通过当前线程对象获取到了ThreadLocalMap对象。此处的ThreadLocalMap是ThreadLocal类的一个静态内部类。如下图所示 : 注意为什么是通过当前线程对象来获取ThreadLocalMap对象呢 因为当前线程持有自己的ThreadLocal对象(该对象调用了set方法)而ThreadLocalMap又是ThreadLocal的一个内部类. 继续接着判断得到的map对象是否为空如下图所示 : 如果不为空就将当前的ThreadLocal对象(this即指在T1类中一开始调用set方法的ThreadLocal对象和该对象调用set方法时放入的数据(value此处是放入的Apple对象。从这里也可以看出如果同一个ThreadLocal对象再次调用set方法会对存入的数据(value)进行替换——即每一个ThreadLocal对象只能为当前线程关联一个数据。 如果为空就创建一个与当前线程对象关联的ThreadLocalMap对象并将目标数据放入(value)。 在set方法调用处设一个断点进入Debug界面后可以看到当前线程对象名字如下图所示 : 在this对象中向下找可以找到一个threadLocals属性发现它就是ThreadLocalMap类型如下图所示 : 我们也可以Thread类的源码中找到这个属性如下图所示 而该属性下的table, 就是实际存放数据的地方(可以看到table是ThreadLocalMap的内部类Entry类型的数组)。当set方法执行完毕后我们可以看到table数组中的Apple对象如下图所示 实际上table就是线程用于管理ThreadLocal实例的容器。 而table数组中每个元素的referent属性(弱引用对象)也就是ThreadLocal对象此处可以看到与之前一致如下 : 2.2 get方法解读 get方法源码如下 : (PS : 注意此处是泛型在方法上的应用而不是自定义泛型方法) 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();} 第一步和set方法一样都是先得到当前的线程对象。为啥因为只有得到了当前线程对象才能找到它的属性threadLocals[ThreadLocal$ThreadLocalMap类型]继而找到该属性维护的table数组(ThreadLocal$ThreadLocalMap$Entry[]类型)然后根据当前线程持有的的ThreadLocal实例找到数组中对应的Entry元素继而拿到它的属性value(保存的数据)。 显然get方法的源码中也的确是这么干的。通过当前线程对象拿到ThreadLocalMap对象我们可以看一下getMap(t)的源码如下图所示 : 之后判断map对象是否为空如果不为空就根据当前持有的ThreadLocal实例去找table数组中对应的Entry元素。继续往下走 : 拿到对应的Entry元素后还要进行判断如果该元素的确是存在的表明当前的ThreadLocal实例已经为当前线程绑定过数据[一个value]), 就取出该Entry元素的value属性此处为Object类型的apple对象然后返回。 以上就是对ThreadLocal的一些浅显解读。感谢阅读