ThreadLocal的源码实现分析

概念

  • 多线程访问共享可变数据时,涉及线程间数据同步问题,并不是所有时间都要用到共享数据,所以出现线程封闭概念
  • 数据都被封闭在各自的线程之中,就不需要同步,这种通过数据封闭在线程中而避免使用同步的技术被称为线程封闭
  • 线程封闭的具体体现有: ThreadLocal , 局部变量

ThreadLocal

定义

  • ThreadLocal 可以看做一个特殊的变量,每个线程都可以给该变量赋值取值,而且每个线程所赋值取值的变量仅仅归自己线程,其他线程无法访问,相当于一个变量多线程使用,等价于多个变量

用法

ThreadLocal var = new ThreadLocal<>();
//当前线程作用
var.set(Object)
var.get()

代码演示

    public static ThreadLocal<String> var = new ThreadLocal<>();
    
    public void closure1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"\t设置了var值\n");
        var.set("main线程值");
        String m = var.get();
        System.out.println(Thread.currentThread().getName()+"\t取var值为:\t"+m+"\n");
        Thread thread1 = new Thread(() -> {
            String threadName = Thread.currentThread().getName();
            String s = var.get();
            System.out.println(threadName+"\t取var值为:\t"+s+"\n");
            var.set("thread1线程值");
            System.out.println(threadName+"\t设置了var值\n");
            String s1 = var.get();
            System.out.println(threadName+"\t取var值为:\t"+s1+"\n");
            System.out.println(threadName+"\t线程结束了\n");
        },"thread1");
        thread1.start();
        Thread.sleep(2000L);
        String m1 = var.get();
        System.out.println(Thread.currentThread().getName()+"\t取var值为:\t"+m1+"\n");
    }

输出

    main	设置了var值

    main	取var值为:	main线程值

    thread1	取var值为:	null

    thread1	设置了var值

    thread1	取var值为:	thread1线程值

    thread1	线程结束了

    main	取var值为:	main线程值

解释

测试代码先定义了公共变量ThreadLocal,main线程先设置了值,获取到main值。thread1尝试获取值。得到null,当thread1设置值后获取到thread1值,当thread1线程结束后main线程仍然获取到mian值,说明每个线程获取到的值都是自己线程特有的

源码分析

大致思路
  • 首先在ThreadLocal中有一个这样的ThreadLocalMap静态内部类
 static class ThreadLocalMap {
        private static final int INITIAL_CAPACITY = 16;
        //这个table用于存放所有的ThreadLocal,
        private ThreadLocal.ThreadLocalMap.Entry[] table;
        private int size = 0;
        private int threshold;

        private void setThreshold(int len) {
            this.threshold = len * 2 / 3;
        }

        private static int nextIndex(int i, int len) {
            return i + 1 < len ? i + 1 : 0;
        }

        private static int prevIndex(int i, int len) {
            return i - 1 >= 0 ? i - 1 : len - 1;
        }

        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            this.table = new ThreadLocal.ThreadLocalMap.Entry[16];
            int i = firstKey.threadLocalHashCode & 15;
            this.table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
            this.size = 1;
            this.setThreshold(16);
        }

        ......
  • 其次在Thread类中有一个ThreadLcoalMap类的变量,并且该变量初值为null,未被初始化
    public class Thread implements Runnable {
    private volatile String name;
    private int priority;
    private boolean daemon;
    private boolean stillborn;
    private long eetop;
    private Runnable target;
    private ThreadGroup group;
    private ClassLoader contextClassLoader;
    private AccessControlContext inheritedAccessControlContext;
    private static int threadInitNumber;
    //这个用于存放不同的ThreadLocal变量及值(entry)
    ThreadLocalMap threadLocals;
    ThreadLocalMap inheritableThreadLocals;
    ......
    
    private Thread(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
        this.daemon = false;
        this.stillborn = false;
        //未初始化
        this.threadLocals = null;
        this.inheritableThreadLocals = null;
  • ThreadLocal执行思路

    • 通过当前的线程获取ThreadLocalMap属性确定该线程有没有定义ThreadLocal变量,即ThreadLocalMap有没有被初始化,如果没有则初始化。初始化的ThreadLocalMap容量为16
    • 如果已经被初始化,则通过获取当前线程的ThreadLocalMap获取当前线程的所有ThreadLocal变量,遍历查找当前ThreadLocal所对应的value值
  • 详细代码

    public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        //如果ThreadLocalMap已经被初始化
        if (map != null) {
            ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
            //如果查找到当前ThreadLocal,则返回
            if (e != null) {
                T result = e.value;
                return result;
            }
            //如果没查到当前ThreadLocal说明没有当前ThreadLocal
        }
        //如果ThreadLocalMap未被初始化或者没有当前ThreadLocal
        return this.setInitialValue();
    }
    
     public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        //如果ThreadLocalMap已经被初始化
        if (map != null) {
            map.set(this, value);
        //否则创建ThreadLocalMap,并赋值
        } else {
            this.createMap(t, value);
        }

    }
    
    //设置初始化值
    private T setInitialValue() {
        T value = this.initialValue();
        Thread t = Thread.currentThread();
        ThreadLocal.ThreadLocalMap map = this.getMap(t);
        //如果ThreadLocalMap已经被初始化
        if (map != null) {
            map.set(this, value);
        } else {
        //否则初始化ThreadLocalMap
            this.createMap(t, value);
        }

        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal)this);
        }

        return value;
    }
    
    private void set(ThreadLocal<?> key, Object value) {
        //获取存放所有ThreadLocal的table
        ThreadLocal.ThreadLocalMap.Entry[] tab = this.table;
        int len = tab.length;
        int i = key.threadLocalHashCode & len - 1;
        
        //遍历所有可能出现该ThreadLocal位置
        for(ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = (ThreadLocal)e.get();
            //如果成功得到该ThreadLocal,则赋值
            if (k == key) {
                e.value = value;
                return;
            }
            //如果k不存在,则将hash值靠后存在的ThreadLocal换到hash值靠前的位置
            //这一部主要是为了提高查询效率
            if (k == null) {
                this.replaceStaleEntry(key, value, i);
                return;
            }
        }
        //将该ThreadLocal加入到ThreadLocalMap中
        tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);
        int sz = ++this.size;
        //优化结构后大于等于2*len/3则rehash
        if (!this.cleanSomeSlots(i, sz) && sz >= this.threshold) {
            this.rehash();
        }

    }
    //替换没用的ThreadLocal,提高查找效率
    private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
            ThreadLocal.ThreadLocalMap.Entry[] tab = this.table;
            int len = tab.length;
            int slotToExpunge = staleSlot;

            ThreadLocal.ThreadLocalMap.Entry e;
            int i;
            //找到最前面值不为空的位置
            for(i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) {
                if (e.get() == null) {
                    slotToExpunge = i;
                }
            }
            //将后面不为空的值换到前面来
            for(i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
                ThreadLocal<?> k = (ThreadLocal)e.get();
                if (k == key) {
                    e.value = value;
                    tab[i] = tab[staleSlot];
                    tab[staleSlot] = e;
                    if (slotToExpunge == staleSlot) {
                        slotToExpunge = i;
                    }

                    this.cleanSomeSlots(this.expungeStaleEntry(slotToExpunge), len);
                    return;
                }

                if (k == null && slotToExpunge == staleSlot) {
                    slotToExpunge = i;
                }
            }
        
    //将当前的ThreadLocal加入其中,并赋予初值,默认为null
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
    }
    
    private void setThreshold(int len) {    this.threshold = len * 2 / 3;}
  • 思路总结:

    1. ThreadLocal实现中心思想是在Thread中放置ThreadLocalMap,查找是不同的Thread所拥有不同的ThreadLocalMap,但是每一个的ThreadLocal可以被多个ThreadLocalMap存放

    2. ThreadLocal同过静态内部类的方式可以访问不同线程的ThreadLocalMap,从而查找自身ThreadLocal对象达到一个类存不同线程的值同时每个线程只能访问自身线程所存放的值的目的

栈封闭

解释

局部变量的固有属性之一就是封闭在线程中,他们位于执行线程的栈中,其他线程无法访问这个栈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值