ThreadLocal

2020-04-30  本文已影响0人  lsh的学习笔记

从使用说起

ThreadLocal<String> name = new ThreadLocal();
ThreadLocal<String> school = new ThreadLocal();
name.set("张三");
System.out.println(name.get());
school.set("张三");
System.out.println(school.get());

从示例代码可以看出,一个ThreadLocal对象只能存储一个数据,如果想存储多个数据,就需要创建多个ThreadLocal对象。

看看源码

class Thread {  
    //内部持有ThreadLocalMap  
    ThreadLocal.ThreadLocalMap threadLocals;
}
// ThreadLocal 类
// get 方法
public void set(T value) {
    Thread t = Thread.currentThread();
    // 注意这个ThreadLocalMap是从当前Thread对象中获取的;
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 注意这里的key,传的是this;
        map.set(this, value);
    else
        createMap(t, value);
}

// 从线程中获取 Map
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

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

// get 方法
public T get() {
    Thread t = Thread.currentThread();
    // 从当前线程中获取 Map;
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 根据 key 获取 value;
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

// 类似,但不是 HashMap,注意构造器;
static class ThreadLocalMap {
    // 内部保存数据的数组
    private Entry[] table;
    // Map的大小
    private int size = 0;

    // 构造器的key,写死存储的Entry的key为ThreadLocal类型;
    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);
    }

    // set方法也限制死了 key;
    private void set(ThreadLocal<?> key, Object value) {
        // 省略代码
    }

    static class Entry extends WeakReference<ThreadLocal<?>> {
        // 关联 ThreadLocal 对象
        Object value;
        // 注意这个构造器,默认传的key就是ThreadLocal对象
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

数据结构

从上面的源码,我们可以分析得:当前线程 Thread 对象内部有一个类似 HashMap 的类,这个类的Entry 保存着当前线程的数据,Entry 的 key 是ThreadLocal对象,value 是用户 set 的值。

如果画图的话,就是如下这张图:

1

内存泄露

使用线程池的时候,线程Thread对象结束是不会销毁的,会再次使用的就可能出现内存泄露 。(在web应用中,每次http请求都是一个线程,tomcat容器配置使用线程池时会出现内存泄漏问题)。而且容易造成数据污染

解决

每次使用后在finally块remove掉。

上一篇下一篇

猜你喜欢

热点阅读