java多线程-ThreadLocal

大纲:

  1. 用法
  2. 源码
  3. 引用及内存泄漏

 

一、用法

ThreadLocal是一个容器,顾名思义就是把一个变量存到线程本地。

实际应用:中事务传播的时候将连接对象存入threadloacl,可以在同一个线程不同的代理类中被拿到。

class Test {
    public static void main(String[] args)  {
        new Thread(new TestLocal()).start();
        new Thread(new TestLocal()).start();
        new Thread(new TestLocal()).start();
    }
}

class TestLocal implements Runnable {
    ThreadLocal<String> localName = new ThreadLocal<>();

    @Override
    public void run() {
        localName.set(Thread.currentThread().getName());
        System.out.println(localName.get());
    }
}

/**结果:
 * Thread-0
 * Thread-1
 * Thread-2
 */

 

 

二、源码

ThreadLocal是线程本地变量,因此每个Thread对象内部都有一个ThreadLocalMap对象,这个对象维护了Entry<Threadlocal,Object>数组,ThreadLocal就以key的形式存在Entry中,value就是ThreadLocal存放的值。

class Thread {
    ThreadLocal.ThreadLocalMap threadLocals = null; //每个线程对象内部维护了一个ThreadLocal.ThreadLocalMap
... }

ThreadLocal主要方法就是set,get

  • set:
public class ThreadLocal<T> {

    static class ThreadLocalMap {...} //ThreadLocalMap是ThreadLocal的静态内部类
    ...

    public void set(T value) {
        Thread t = Thread.currentThread(); //拿到当前线程
        ThreadLocalMap map = getMap(t); //取出线程维护的ThreadLocalMap
        if (map != null)
            map.set(this, value); //ThreadLocalMap的key为当前ThreadLocal对象,value就是我们需要存储的变量
        else
            createMap(t, value); //该线程第一次使用ThreadLocal.set时创建ThreadLocalMap对象,并赋值。
    }

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

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

  ... }
  • get:
public class ThreadLocal<T> {

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t); //从当前线程取出ThreadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); //以当前ThreadLocal对象为key取出ThreadLocalMap.Entry
            if (e != null) {
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue(); //如果这个ThreadLocal对象没有赋值直接get,会给它赋值为null并返回。
    }

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

    protected T initialValue() {
        return null;
    }

  ... }

ThreadLocal get、set思想小结:

  1. 拿到当前线程对象。
  2. 拿到线程对象内部维护的ThreadLocalMap对象。
  3. 一个线程对象中只有一个ThreadLocalMap对象,所有ThreadLocal对象及这个ThreadLocal对象存储的值都以key-value的形式存在ThreadLocalMap中。(ThreadLocalMap的key是ThreadLocal对象,value是需要存储的变量。)  

 

三、引用及内存泄漏

java里面4种引用类型:强、软、弱、虚(堆外内存,不归jvm管理)

强:有强引用情况下,oom也不会被回收

软:仅(注意是仅)有软引用的情况下,内存空间不够的情况下会被回收。

虚:仅有虚引用的情况下,每次GC都会被回收。

static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
...
}

在ThreadLocalMap中存放键值对的对象是一个内部类,构造方法super(k),标识了threadlocal在Entry中是一个虚引用。

ThreadLocalMap在的生命周期与Thread一致,使用完threadlocal,如果不remove操作的话,会一致保存在ThreadLocalMap的entry中,就会存在内存泄漏问题。这时Entry中的弱引用就会被下次GC回收,而null key的Entry会在之后的任意set,get,remove方法中被释放,从而避免了内存泄漏问题。

 

posted @ 2019-03-04 15:49  扶不起的刘阿斗  阅读(1472)  评论(0编辑  收藏  举报