• ThreadLocal源码分析与实践


    ThreadLocal是什么?

    ThreadLocal是一个线程内部存储类,提供线程内部存储功能,在一个ThreadLocal对象中,每一个线程都存储各自独立的数据,互不干扰

    示例如下:

    public class ThreadLocalTest {
    
        @Test
        public void test() throws InterruptedException {
            ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
            Thread thread1 = new Thread(new MyTask(threadLocal, 10));
            Thread thread2 = new Thread(new MyTask(threadLocal, 100));
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
        }
    
    }
    
    class MyTask implements Runnable {
    
        private ThreadLocal<Integer> threadLocal;
        private int value;
    
        public MyTask(ThreadLocal<Integer> threadLocal, int value) {
            this.threadLocal = threadLocal;
            this.value = value;
        }
    
        @Override
        public void run() {
            threadLocal.set(++value);
            System.out.println(threadLocal.get());
        }
    }

    源码分析

    get()方法

      public T get() {
            // ThreadLocalMap是ThreadLocal中的一个类内部类,而每一个Thread实例都拥有一个ThreadLocalMap实例变量用来存储线程的内部数据
         Thread t = Thread.currentThread();
            // 获取线程实例变量ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            // 如果map!=null则表示Thread中的ThreadLocalMap之前已经实例过
            if (map != null) {
                // ThreadLocalMap实例中有数组实例Entry[] table用于存储真正的数据,key为ThreadLocal,value为存储的值,所以一个线程可以同时维护多个ThreadLocal
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
    // 初始化ThreadLocalMap实例
    return setInitialValue(); }   
      
    // getMap就是获取当前线程下的ThreadLocalMap实例   ThreadLocalMap getMap(Thread t) {
    return t.threadLocals; }
    // 根据ThreadLocal获取对应的value   
    private Entry getEntry(ThreadLocal<?> key) {
    // 内部存储为数组形式,通过就算key的hashCode进而确认索引位置
    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); } // 如果map为null,则初始化ThreadLocalMap   private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }

     真实数据是存储在Thread对象的ThreadLocalMap实例中,所以每个线程都维护自己的内部数据,当有多个ThreadLocal时,每个ThreadLocal根据hashCode匹配到一个索引存储

     set()方法

      public void set(T value)         
         Thread t = Thread.currentThread();

    ThreadLocalMap map
    = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }   private void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length;
    // 计算索引值
    int i = key.threadLocalHashCode & (len-1);         // ThreadLocalMap使用线性探测法来解决哈希冲突,假设计算后的i为10,该位置k不为null且与key不相等,则匹配索引为11的位置,一直重复下去直到可以插入为止
            // 当然这里不会出现走了一个循环还没有空位置可以插入
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get();           // 将新设置的值替换旧值 if (k == key) { e.value = value; return; }           // 该位置没被占用,则存入新的值 if (k == null) { replaceStaleEntry(key, value, i); return; } }        // 查到为空的位置插入数据  tab[i] = new Entry(key, value); int sz = ++size;
           
           // 判断是否需要扩容
    if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }

     应用实例:

    当使用spring框架支持数据库事务时,需要将获取的数据库连接与当前线程绑定在一起,这时应用的就是ThreadLocal保存线程内部数据的特性,多次操作数据库使用的都是同个连接,这样才能保证事务的完成。

  • 相关阅读:
    JDK8新特性
    控制线程同步的方法
    Java面向对象(一)20170517
    Java语言基础-递归
    勒索病毒应对措施
    JAVA application 初步20170515
    JAVA语言基础(二)20170511
    JAVA语言基础(二)20170510
    JAVA学习概述(一)20170508
    我有一个读绘本的妈妈
  • 原文地址:https://www.cnblogs.com/xiguadadage/p/13718454.html
Copyright © 2020-2023  润新知