Android收藏

ThreadLocal介绍

2022-02-02  本文已影响0人  喏喏2021

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值
上一篇下一篇

猜你喜欢

热点阅读