《Java高并发程序设计》 --ThreadLocal

public class ThreadLocalDemo {
    static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) {
        Thread[] threads = new Thread[5];
        for (int i = 0; i < 5; i++) {
            threads[i] = new Thread(()->{
                Integer num = local.get();
                local.set(num+ 5);
                System.out.println(Thread.currentThread().getName()+ "-" + num);
            });
        }

        for (int i = 0; i < 5; i++) {
            threads[i].start();
        }
    }
}

代码输出:

Thread-0-0
Thread-3-0
Thread-4-0
Thread-2-0
Thread-1-0

解释:

各个线程获取的值不会受到其他线程修改值的影响


 

 

原理大致如图:

 

对流程的说明:

 对于新开启的一个线程Thread,内部有一个ThreadLocalMap对象(类似于hash map)存储供当前线程使用的自定义的Object value。

 该map的key为ThreadLocal实例,值为用户自定义使用的值。

 

下面就解析一下ThreadLocal的set方法

 

这个方法分为两步走:

  1. 创建新的ThreadLocalMap
  2. 将当前的ThreadLocal放入到已有的ThreadLocalMap中 

看第一步:

    

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

就是ThreadLocalMap的构造函数啦, 给table赋值一个 初始化大小的Entry数组,再根据threadLocal的hashcode和长度进行&比较获取table中的下标,然后插入新的Entry值。

 

看第二步啦:

 

 

这有三个if,一个for:

     for: 遍历ThreadLocalMap中的Entry数组,找到不为空的进行判断

  1. 如果是同一个坑的key,直接覆盖value
  2. 如果k为空,也就是threadLocal为空但是value还存在的情况,

注释:当一个ThreadLocal失去强引用,生命周期只能存活到下次gc前,此时ThreadLocalMap中就会出现key为null的Entry,当前线程无法结束,这些key为null的Entry的value就会一直存在一条强引用链,造成内存泄露。

 

    for 循环遍历结束后,将在对应的table位置新建一个entry存放新值。  

    并处理那些标记为需要清除的slot entry ,并进行rehash

 

 

 

  

 

posted @ 2021-01-08 00:02  一只喜鹊  阅读(92)  评论(0编辑  收藏  举报