在activeJDBC框架内部的实现中看到了 ThreadLocal 这个类,记录下了每个线程独有的连接

private static final ThreadLocal<HashMap<String, Connection>> connectionsTL = new ThreadLocal<>();

感觉是个知识点,就打开源码看看了。先看一下源码里的解释

这个鸟文,瞎翻译一下,就是:

先跑一下用法吧,

package com.test.threadlocal;

public class TestController {

    private static int index = 0;
    private static String str = "这个字符串是每个线程共享的";
    // 这个变量,看似是一个类的静态属性,实则是每个线程有自己独有的区域
    private static ThreadLocal<String> threadStr = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "main线程专享";
        }
    };

    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 3; i++) {
            Thread t = new MyThread();
            t.start();
            t.join();
        }

        System.out.println(str);
        System.out.println(threadStr.get());
    }

    static class MyThread extends Thread{

        @Override
        public void run() {
            index++;
            str = "第" + index + "个str";
            threadStr.set("第" + index + "个threadStr");
        }


    }

}

这个例子中,从str和threadStr变量的打印结果可以看出来。str被所有的线程读和写,threadStr在每个线程内部开辟了一块线程专享的区域。接下来,我们看一下具体实现。
先看一下构造函数

     /**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and
     * inheritableThreadLocals).  The ThreadLocal objects act as keys,
     * searched via threadLocalHashCode.  This is a custom hash code
     * (useful only within ThreadLocalMaps) that eliminates collisions
     * in the common case where consecutively constructed ThreadLocals
     * are used by the same threads, while remaining well-behaved in
     * less common cases.
     */
    private final int threadLocalHashCode = nextHashCode();
     /**
     * Creates a thread local variable.
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
    }

构造函数是空的,但是,该类有一个私有整型常量threadLocalHashCodenextHashCode()方法我们就不看了,省的一如源码深似海。看鸟文的话,大概就是每new一个ThreadLocal变量的时候,就会生成一个散列码,该码非极端情况下与某个整数取模后不容易冲突(这句话有点迷吧,其实我也不懂)
然后看一下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);
    }

容易看出,这个方法设置每个线程自己的value,相当于当前线程是key,然后得出一个ThreadLocalMap。显然,这个map用来保存线程内部的值,既然是map当然每个线程可以保存多个数值了,该map的value我们猜一下就是我要保存的具体的值,估计是用Object类声明的。那key是什么呢?我们看下ThreadLocalMap类的构造方法。

/**
         * 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);
        }

我的天!这个类没有继承我们想象中的HashMap,或者是ConcurrentMap,但是看过,Map的内部实现的同学应该可以发现,这个Map的实现和HashMap的实现简直就是小巫见大巫,有没有。它在构造函数中做了如下几步:

  1. 初始化一个大小为16的Entry数组
  2. 通过上面说过的很迷的HashCode散列值与15取模得到将要存储在数组中的索引值
  3. 构造Entry,然后保存进去
  4. 长度设置为1
  5. 设置要扩容的限制大小为16的2/3

我们看到这个不就是用数组实现的Map嘛,看过HashMap实现的我们,觉得洒洒水啦。
Map的set和get方法就不分析了。ThreadLocal的get方法我们还是要贴出来的,毕竟是我们主要分析的东西

/**
     * 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);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

可见,是获取到当前线程,用作key获取到Map,然后用当前this获取到Entry实体。最后当然获取到了存储的value。

我编码,我快乐~

11-19 06:08