java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > ThreadLocal和ThreadLocalMap

java中ThreadLocal和ThreadLocalMap浅析

作者:20481024

这篇文章主要介绍了java中ThreadLocal和ThreadLocalMap浅析,ThreadLocal类用来设置线程私有变量 本身不储存值 主要提供自身引用 和 操作ThreadLocalMap 属性值得方法,需要的朋友可以参考下

ThreadLocal和ThreadLocalMap

先发一个最常见的图

在这里插入图片描述

关键字 ThreadLocal ThreadLocalMap Thread

1 概念 ThreadLocal

在这里插入图片描述

理解:ThreadLocal类用来设置线程私有变量 本身不储存值 主要提供自身引用 和 操作ThreadLocalMap 属性值得方法,使用ThreadLocal会通过ThreadLocal的引用定位到到堆中Thread的类ThreadLocalMap里散列表里的值 从而达到线程私有

目的:解决多线程使用共享对象的问题 空间换时间

2 存储形式

ThreadLocalMap(ThreadLocal 的静态内部类 由ThreadLocal类来维护和创建)中

a:和普通Hashmap类似存储在一个数组内,但与hashmap使用的拉链法解决散列冲突的是 ThreadLocalMap使用开放地址法 //开放地址法缺点 : -空间利用率低 开发地址发会在散列冲突时寻找下一个可存入的槽点 为了避免冲突 负载因子会设置的相对较小 还使用的原因是 ThreadLocalMap的散列值均匀 且经常增删 纯数组较方便 节点数量少 时也能节省掉指针域带来的空间开销

b : 数组 初始容量16,负载因子2/3

c : node节点 的key封装了WeakReference 用于回收

d : 数组位置计算

ThreadLocalMap属性

        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        //初始容量16
        private static final int INITIAL_CAPACITY = 16;
        //散列表
        private Entry[] table;
        //entry 有效数量 
        private int size = 0;
        //负载因子
        private int threshold; 

ThreadLocalMap设置ThreadLocal 变量

    private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            //与运算  & (len-1) 这就是为什么 要求数组len 要求2的n次幂 
            //因为len减一后最后一个bit是1 与运算计算出来的数值下标 能保证全覆盖 
            //否者数组有效位会减半 
            //如果是hashmap 计算完下标后 会增加链表 或红黑树的查找计算量 
            int i = key.threadLocalHashCode & (len-1);
            // 从下标位置开始向后循环搜索  不会死循环  有扩容因子 必定有空余槽点
            for (Entry e = tab[i];   e != null;  e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                //一种情况 是当前引用 返回值
                if (k == key) {
                    e.value = value;
                    return;
                }
                //槽点被GC掉 重设状态 
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
			//槽点为空 设置value
            tab[i] = new Entry(key, value);
            //设置ThreadLocal数量
            int sz = ++size;
			//没有可清理的槽点 并且数量大于负载因子 rehash
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

3 储存位置

储存在Thread中的两个ThreadLocalMap变量

在这里插入图片描述

4 储存时机

threadLocals 在ThreadLocal对象方法get中去创建 也由ThreadLocal来维护

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            **createMap**(t, value);
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = **new ThreadLocalMap**(this, firstValue);
    }

inheritableThreadLocals 和ThreadLocal类似 InheritableThreadLocal重写了createMap方法

void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

inheritableThreadLocals 作用是将ThreadLocalMap传递给子线程

在这里插入图片描述

init方法中 条件满足后直接为子线程创建ThreadLocalMap

在这里插入图片描述

重要

a: 仅在初始化子线程的时候会传递 中途改变副线程的inheritableThreadLocals 变量 不会将影响结果传递到子线程 。

b:使用线程池要注意 线程不回收 尽量避免使用父线程的inheritableThreadLocals 导致错误 

到此这篇关于java中ThreadLocal和ThreadLocalMap浅析的文章就介绍到这了,更多相关ThreadLocal和ThreadLocalMap内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文