ThreadLocal介绍

作者: 喏喏2021 | 来源:发表于2022-02-02 23:56 被阅读0次

    1. 简单使用

    package thread;
    
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    
    public class ThreadLocalTest {
           //定义一个threadLocal共享亦是
        private static final ThreadLocal<String> userIdThreadLocal = new ThreadLocal<String>();
        public static void main(String[] args) {
            String mainUserId = UUID.randomUUID().toString();
            System.out.println("main thread userId set:" + mainUserId);
            //主线程中设置userId
            userIdThreadLocal.set(mainUserId);
            
            Thread t1 = new Thread(()->{
                String t1UserId = UUID.randomUUID().toString();
                System.out.println("t1 thread userId set:" + mainUserId);
                //t1线程中设置userId
                userIdThreadLocal.set(t1UserId);
                try {
                    TimeUnit.MICROSECONDS.sleep(2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //t1线程中打印userId
                System.out.println("t1 thread userId get:" + userIdThreadLocal.get());
            });
            t1.start();
            //主线程中打印userId
            System.out.println("main thread userId get:" + userIdThreadLocal.get());
        }
    }
    //main thread userId set:c7eee748-a2af-4686-9b26-ba930aa3c2fd
    //main thread userId get:c7eee748-a2af-4686-9b26-ba930aa3c2fd
    //t1 thread userId set:c7eee748-a2af-4686-9b26-ba930aa3c2fd
    //t1 thread userId get:b43f281f-19b0-4b4f-92b4-3f10cba3612d
    

    可以看到,同一个线程中设置完userId,在线程中的其他地方可以读取出来使用

    2. 实现原理

    我们先看一下ThreadLocal中,上面用到的两个方法:get(),set()
    这里我们要先看一个ThreadLocal中的嵌套类ThreadLocalMap

    static class ThreadLocalMap {
    
            /**
             * The entries in this hash map extend WeakReference, using
             * its main ref field as the key (which is always a
             * ThreadLocal object).  Note that null keys (i.e. entry.get()
             * == null) mean that the key is no longer referenced, so the
             * entry can be expunged from table.  Such entries are referred to
             * as "stale entries" in the code that follows.
             */
            static class Entry extends WeakReference<ThreadLocal> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
            /**
             * The initial capacity -- MUST be a power of two.
             */
            private static final int INITIAL_CAPACITY = 16;
    
            /**
             * The table, resized as necessary.
             * table.length MUST always be a power of two.
             */
            private Entry[] table;
    }
    

    说明:

    1. 我们可以看到ThreadLocalMap内部还嵌套了一个Entry的内部类,它是继承了弱引用,Entry本身只有一个value值
    2. ThreadLocalMap中,有一个Entry类型的数组,Entry[] table,这里保存着各ThreadLocal的key,以及对应的value值

    下面再看一下这两个方法:

    /**
         * Returns the value in the current thread's copy of this
         * thread-local variable.  If the variable has no value for the
         * current thread, it is first initialized to the value returned
         * by an invocation of the {@link #initialValue} method.
         *
         * @return the current thread's value of this thread-local
         */
        public T get() {
            Thread t = Thread.currentThread();  //获得当前线程
            ThreadLocalMap map = getMap(t);  //根据线程获得threadLocalMap变量
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this); //获得当前threadlocal对应的entry值
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result; //返回entry中的value值
                }
            }
            return setInitialValue();
        }
    /**
         * 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); //返回当前线程中的threadlocalMap变量
            if (map != null)
                map.set(this, value); //根据当前threadlocal键,设置value值
            else
                createMap(t, value);
        }
    

    从上面可以看出,我们根据线程,就可以得到threadLocalMap变量,
    我们看一下线程的源码可以看到如下两行定义:


    image.png

    也就是说,实际threadLocalMap值是存储在当前线程中的

    3. 总结

    1. 与synchronized锁的机制不同,threadLocal主要是在每个线程中保存着一份entry值,包含着当前threadlocal和对应的value值
    2. 每次取值时,都可以从当前线程的threadLocalMap中,根据threadLocal取得对应的value值

    相关文章

      网友评论

        本文标题:ThreadLocal介绍

        本文链接:https://www.haomeiwen.com/subject/gcyjkrtx.html