0
点赞
收藏
分享

微信扫一扫

ThreadLocal简单介绍

全栈顾问 2022-04-18 阅读 32
java

文章目录

1、简介

先看一张图,下面中每个资源1都是不对其他线程可见的,都是独属于各自的线程
在这里插入图片描述
出现的场景:

  1. 每一个线程都可能产生一个全局变量,并且值都是专属于这一个线程的,别的线程不能使用,供线程调用后面的方法取出使用,避免重复传参数,当然你说我就专门当方法参数传,那也可以不用
  2. 如果涉及到不安全的工具类的使用,可能会造成问题,比如simpleDateFormat
  3. 剩下的场景就自己搜索吧

2、之后看一个代码示例,

 		static ThreadLocal<String> localVar = new ThreadLocal<>();
        static void print(String str) {
            //打印当前线程中本地内存中本地变量的值
            System.out.println(str + " :" + localVar.get());
            //清除本地内存中的本地变量
            localVar.remove();
        }
        public static void main(String[] args) {
            Thread t1  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程1中本地变量的值
                    localVar.set("线程1中存储在ThreadLocal中的字符串");
                    //调用打印方法
                    print("thread1");
                    //打印本地变量
                    System.out.println("after remove : " + localVar.get());
                }
            });

            Thread t2  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //设置线程1中本地变量的值
                    localVar.set("线程2中存储在ThreadLocal中的字符串");
                    //调用打印方法
                    print("thread2");
                    //打印本地变量
                    System.out.println("after remove : " + localVar.get());
                }
            });

            t1.start();
            t2.start();
        }

下面的是输出结果:下面只是一种输出结果,thread2thread1的输出顺序不固定
在这里插入图片描述

3、ThreadLocal在java中的是存储结构

(1)set方法

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
     /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
     /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
     /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
     /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

下面是Thread.java

  /**
     * Returns a reference to the currently executing thread object.
     *
     * @return  the currently executing thread.
     */
    public static native Thread currentThread();
     /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

看上面两段代码就知道了,实际上是创建了一个ThreadLocalMap放到了ThreadLocal.ThreadLocalMap threadLocals 中,

ThreadLocalMap里面是用一个Entry[] table数组当前线程的局部变量,就像下面的关系图一样

Thread,ThreadLoacl,ThreadeLocalMap三个类之间的关系

在这里插入图片描述

(2)get方法

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	//ThreadLocalMap.Entry相当于table数组中的某个值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    private Entry getEntry(ThreadLocal<?> key) {
    	//通过threadLocalHashCode和数组长度找出此线程在table数组中
    	//哪个索引位置(i)存储的数据
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
           return e;
        else
            return getEntryAfterMiss(key, i, e);
        } 

4、为什么ThreadLocalMap中要用数组存储?

1、一个Thread只有一个ThreadLocals(通过看Thread的源码知道)
2、所以同一个线程得到的ThreadLocalMap都是同一个对象(通过getMap方法知道)
3、如果同一个线程创建了多个ThreadLocal,也都是存储在同一个ThreadLocalMap中,所以ThreadLocalMap中需要用Entiy数组来存储

一个线程多ThreadLocal代码示例

  static ThreadLocal<String> localVar1 = new ThreadLocal<>();
        static ThreadLocal<String> localVar2 = new ThreadLocal<>();

        static void print(String str) throws InterruptedException {
            //打印当前线程中本地内存中本地变量的值
            System.out.println(str + " :" + localVar1.get());
            System.out.println(str + " :" + localVar2.get());
            //清除本地内存中的本地变量
            localVar1.remove();
            localVar2.remove();
        }

        public static void main(String[] args) {
            Thread t1  = new Thread(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    localVar1.remove();
                    localVar2.remove();
                    //设置线程1中本地变量的值
                    localVar1.set("线程1中存储在ThreadLocal1中的字符串");
                    localVar2.set("线程1中存储在ThreadLocal2中的字符串");
                    //调用打印方法
                    print("thread1");
                    //打印本地变量
                    System.out.println("after remove : " + localVar1.get());
                }
            });
            t1.start();
        }

debug图
在这里插入图片描述
所以两个ThreadLocal的数据都是存储在同一个ThreadLocalMap中,只是在table中的索引位置不同
下面是两个ThreadLocal如何获得各自在ThreadLocalMap中数组的存储的数据的索引
在这里插入图片描述
在这里插入图片描述
因为ThreadLocalthreadLocalHashCode都不一样,这样就可以得到各自的数据

举报

相关推荐

0 条评论