多线程学习---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变量中的用户登录信息等等很多使用场景

posted @ 2021-04-17 10:53  莫慌*急啥  阅读(119)  评论(0)    收藏  举报