《Java后端知识体系》系列之ThreadLocal原理解析

ThreadLocal的原理解析

  • ThreadLocal:

    • ThreadLocal
      • 概念:多线程访问同一个共享变量时容易出现并发问题,特别是多个线程需要对一个共享变量写入时, 为了保证线程安全,在访问共享变量时需要进行适当的同步。

      • 同步的措施一般是加锁,但是使用加锁的方式增加了性能的损耗,因此可以使用ThreadLocal来实现。

      • ThreadLocal是JDK提供的,提供了线程本地变量,也就是如果创建了一个TreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。因此当多个线程操作这个ThreadLocal变量时,其实操作的是自己本地内存里面的变量,从而避免了线程安全问题。

        public class ThreadLocalTest {
            //创建ThreadLocal变量
            static ThreadLocal<String> threadLocal = new ThreadLocal<>();
            //print函数
            static void print(String string){
                //打印当前线程本地内存中的threadLocal
                System.out.println(string+":"+threadLocal.get());
                //清除当前线程本地内存中的threadLocal
        //        threadLocal.remove();
            }
        
            public static void main(String[] args) {
                //创建线程
                Thread threadA = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //设置线程A中本地变量threadLocal的值
                        threadLocal.set("threadA");
                        //调用打印函数
                        print("threadA");
                        System.out.println("threadA remove "+threadLocal.get());
                    }
                });
                //创建线程
                Thread threadB = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //设置线程B中本地变量threadLocal的值
                        threadLocal.set("threadB");
                        //调用打印函数
                        print("threadB");
                        System.out.println("threadB remove "+threadLocal.get());
                    }
                });
                threadA.start();
                threadB.start();
            }
        }
        

        在这里插入图片描述
        执行threadLocal.remove()之后结果
        在这里插入图片描述

      • 以上代码中创建了一个共享变量threadLocal,两个线程,线程A和线程B,当我们在线程A中对共享变量设置值时,并不会影响线程B中threadLocal的值,线程中通过set方法设置threadLocal的值,其实设置的是线程中本地内存中的一个副本,这个副本线程B是访问不了的。同时通过get获取的是当前线程本地内存中的值。

      • 实现原理
        ThreadLocal的结构在这里插入图片描述

      • 通过图中我们可以看到Thread类中有一个threadLocalsinheritableThreadLocals,它们都是ThreadLocalMap类型的变量,而ThreadLocalMap是一个定制化的HashMap。在默认情况下每个线程中的threadLocals和inheritableThreadLocals都为null,当线程第一次调用set或get时才会创建它们。

      • 每个线程的本地变量并不存在ThreadLocal实例里面,而是存放在线程中的threadLocals变量中,因此ThreadLocal就是一个工具壳,通过set将值放入线程中的threadLocals中,通过get将值从threadLocals中取出来,也可以通过调用remove将当前线程的threadLocals中的值删除。

      • 那么为什么threadLocals为什么被设置成map结构,因为一个线程可能关联多个ThreadLocal变量

      • 每个线程内部都有一个threadLocals的成员变量,该变量类型为HashMap,key为ThreadLocal实例对象的引用value则是需要设置的每个线程的本地变量都存放在线程自己的内存变量threadLocals中,如果线程不死亡,那么该变量会一直存在,因此会造成内存溢出,因此使用完毕应当调用remove删除threadLoccals中的变量。

        从源码中我们也可以看到是通过threadLocals来实现的

            public void set(T value) {
            	//获取当前线程
                Thread t = Thread.currentThread();
                调用getMap,当前线程作为key,去查找对应的线程变量
                ThreadLocalMap map = getMap(t);
                //如果map不为空,则调用set方法,key为当前ThreadLocal的实例对象引用,value是传递的值。
                if (map != null)
                    map.set(this, value);
                else
                	//如果是第一次则调用
                    createMap(t, value);
            }
            ThreadLocalMap getMap(Thread t) {
            	//在getMap中获取的是当前线程的threadLocals
                return t.threadLocals;
            }
        

        另一个类lnheritableThreadLocal类是为了解决子线程可以访问父线程中设置的本地变量

        public class InheritableThreadLocal<T> extends ThreadLocal<T> {
            /**
           
             *
             * @param parentValue the parent thread's value
             * @return the child thread's initial value
             */
            protected T childValue(T parentValue) {
                return parentValue;
            }
        
            /**
             * Get the map associated with a ThreadLocal.
             *
             * @param t the current thread
             */
            ThreadLocalMap getMap(Thread t) {
               return t.inheritableThreadLocals;
            }
        
            /**
             * Create the map associated with a ThreadLocal.
             *
             * @param t the current thread
             * @param firstValue value for the initial entry of the table.
             */
            void createMap(Thread t, T firstValue) {
                t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
            }
        }
        
        • InheritableThreadLocal继承ThreadLocal,并重写三个方法。因此当调用set方法时,创建的是当前线程的inheritableThreadLocals变量的实例而不再是threadLocals,调用get方法时获取当前线程内部的map变量时,获取的是inheritableThreadLocals而不再是threadLocals。因此在InheritableThreadLocal的世界里,变量inheritableThreadLocals替代了threadLocals。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值