多线程学习---ThreadLocal
ThreadLocal提供了线程本地变量,也就是说如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量的时候,实际操作的是自己本地内存里面的变量,从而避免了线程同步问题,创建一个ThreadLocal变量后,每个线程都会复制一个变量到自己的本地内存
例子:
public class Main { //创建一个Thread'Local对象 static ThreadLocal<String> local=new ThreadLocal<>(); public static void print(String str){ System.out.println("线程"+str+"的值"+local.get()); local.remove(); } public static void main(String[] args) throws Exception { Thread threadA=new Thread(new Runnable() { @Override public void run() { //设置local对象的值 local.set("这是A线程设置的值"); print("A"); System.out.println("threadA的值删除后是"+local.get()); } }); Thread threadB=new Thread(new Runnable() { @Override public void run() { //设置local对象的值 local.set("这是线程B的值"); print("B"); System.out.println("线程B的值是"+local.get()); } }); threadA.start(); threadB.start(); } }
运行结果:

代码中,线程A通过set方法设置了local的值,这其实设置的是线程A本地内存中的一个副本,这个副本线程B是访问不了的,然后调用了print函数,通过get方法获取了当前线程本地内存中的值,然后执行删除操作;线程B也和线程A做了相同的操作
具体分析:Thread类中有一个threadLocals和一个inheritableThreadLocals两个参数,它们都是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap,在默认情况下,每个线程中的这两个变量都为null,只有当前线程第一次调用ThreadLocal的set或者get方法时才会创建他们。其实每个线程的本地变量不是存放在ThreadLocal实例里面,而是存放在调用线程的threadLocals变量里面,也就是说,ThreadLocal类型的本地变量存放在具体的线程内存空间中。ThreadLocal就是一个工具壳,他通过set方法把value值放入调用线程的threadLocals里面并存放起来,当调用线程调用它的get方法的时候,再从当前线程的threadLocals变量里面将其拿出来使用,如果调用线程一直不终止,那么这个本地变量会一直存放在调用线程的threadLocals变量里面,所以当不需要使用本地变量的时候可以通过调用ThreadLocal变量的remove方法,从当前线程的threadLocals里面删除该本地变量。
ThreadLocal
set、get及remove的实现逻辑
1、void set(T value)

通过源码可知ThreadLocal类的set()方法的实现过程,首先获取调用多线程,然后使用当前线程作文参数调用getMap()方法,而在getMap()方法的作用是获取当前线程自己的变量threadLocals,threadlocal变量被绑定到了线程的成员变量上;如果getMap返回值不为空,则把value值设置到threadLocal中,也就是把当前变量值放入当前线程的内存变量threadLocals中。threadLocals是一个HashMap结构,其中的key就是当前ThreadLocal的实例对象引用,value是通过set方法传递的值;如果getMap返回值为空,则说明第一次调用set方法,这时通过createMap方法创建当前线程的threadLocals变量

2、T get()

首先获取当前线程实例和获取当前线程的threadLocals变量,如果当前线程的threadLocals不为null,则直接返回当前设置绑定的本地变量,否则执行setInitialvalue()进行初始化


首先初始化一个null值,在获取当前线程的的的threadLocals变量,如果变量不为空,则设置当前线程的本地变量值为null,否则调用createMap方法创建当前线程的createMap变量
3、remove()

如果当前线程的threadLocals变量不为null,则删除当前线程中指定ThreadLocal实例的本地变量
注意:在每个线程内部都有一个名为ThreadLocals的成员标量,该变量的类型为HashMap,其中key为我们定义的TheadLocal变量的this引用,value则是我们使用set方法设置的值,每个线程的本地变量存放在线程自己的内存变量threadLocals中,如果当前线程一直不消亡,那么这些本地变量会一直存在,所以可能会造成内存溢出,因此使用完毕后要调用ThreadLocal的remove方法删除对应线程的threadLocals中的本地变量
ThreadLocal不支持继承性
也就是说,同一个ThreadLocal变量在父线程中被设置值之后,在子线程中是获取不到的
例如:
public class Main { //创建一个Thread'Local对象 static ThreadLocal<String> local=new ThreadLocal<>(); public static void main(String[] str){ local.set("这是主线程设置的值"); Thread threadA=new Thread(new Runnable() { @Override public void run() { System.out.println("子线程输出local的值"+local.get()); } }); threadA.start(); System.out.println("主线程输出local的值"+local.get()); } }
执行结果:

从执行结果中可以看出,主线程设置的值,子线程并没有获取到,所以ThreadLocal不支持继承性
inheritableThreadLocals
为了解决ThreadLocal不能支持继承性,所以出现了InheritableThreadLocal类

通过代码可知,InheritableThreadLocal类继承ThreadLocal类,然后重写了createMap()、getMap()和childvalue()方法,所以使用此类创建的是inheritableThreadLocals实例,而不再是threadLocals实例
将上边ThreadLocal不支持继承性的示例代码改为使用InheritableThreadLocal

执行结果:

可以看到,子线程也可以访问主线程设置的local的值了
子线程获取父线程的threadlocal变量的使用场景,比如子线程需要使用存放在threadlocal变量中的用户登录信息等等很多使用场景

浙公网安备 33010602011771号