ThreadLocal原理

JAVA基础:ThreadLocal原理解析

  • ThreadLocal用途
  • ThreadLocal原理
    • 看似一个容器
    • 实际的容器
    • ThreadLocalMap
    • 弱引用
    • 引用关系图
    • 对象的生命周期

ThreadLocal用途

当需要声明一个Singleton的对象,想在多个线程中使用这个声明,每个线程使用自己独立的对象时。或者需要在线程内存储一个全局变量,仅当前线程可以访问。

ThreadLocal原理

看似一个容器

最早看到ThreadLocal的用法时,感觉好像是myThreadLocal是一个容器,它存储了我们放进去的对象,可以取出也可以移除。直到看了源码后才发现事实并不是这样,myThreadLocal只是一个KEY,真正的容器另有其人。

public class Main {


    public static void main(String[] args) {
        ThreadLocal<MyObject> myThreadLocal = new ThreadLocal();
        myThreadLocal.set(new MyObject(12));
        System.out.println(myThreadLocal.get());
        myThreadLocal.remove();
    }


}


class MyObject{
    
    private Integer myValue;
    
    MyObject(int x){
        this.myValue = x;
    }

    public Integer getMyValue() {
        return myValue;
    }

    public void setMyValue(Integer myValue) {
        this.myValue = myValue;
    }
}

实际的容器

下面来看一些源码

	//设置值
 	public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
//获取值
	public T get() {
		// 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程的ThreadLocalMap对象(真正的容器)
        ThreadLocalMap map = getMap(t);
        if (map != null) {
          // 以myThreadLocal对象自己为key,来获取Entry,Entry中存储了值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

从上面的代码可以看出,实际上我们在"往myThreadLocal里放值或取值"时,其实是以myThreadLocal为key,在当前线程中的ThreadLocalMap对象中进行get和set操作。所以真正的容器是ThreadLocalMap对象。

ThreadLocalMap

我们看Thread的代码时,了解到每个Thread都有一个属性,它存储了当前线程的ThreadLocalMap对象,ThreadLocalMap中存储了所有的本地线程的对象。

 	/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
   ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal类是ThreadLocal内的一个静态内部类,它是自己实现了一个hash map,和hash map的数据结构相似,但是我们注意到它的Entry是一个弱引用WeakReference。它实现了一个简易版的WeakHashMap。这里需要讲一下弱引用是什么,为什么使用弱引用。

弱引用

概念上大家应该都清楚,就是如果它引用的对象只有它一个人在引用,那么在下次垃圾回收触发时,将回收它引用的对象,它将指向null。也就是说,弱引用不会影响对象的GC回收。
考虑下面的场景:现在有一个容器,或者有一个缓存,我们在容器外将对象A的所有引用置为null,来表示这个对象用不到了,如果是强引用:那么我们还需要在容器中手动将该对象A的引用置为空,这样对象A才能被回收。

关键点是:“从容器中移除这个不用的对象”这个工作我们不想自己完成,我们希望的是:当除了这个容器引用对象A以外,没有其他引用的时候就可以回收这个对象了。

这种情况,我们就可以在容器中使用弱引用指向对象A即可。
这就是WeakHashMap的作用。

引用关系图

ThreadLocal原理_第1张图片
如图所示,ThreadLocalMap是一个数组散列,每一个槽引用了一个Entry对象,这个对象继承了WeakReference,是一个弱引用。它指向的ThreadLocal对象如果外部没有引用指向此对象时,这个myThreadLocal对象会被回收。但是这里引用的myObject是强引用,区别是就算已经没有其他引用指向myObject时,myObject也不会被回收。
所以使用ThreadLocal里的remove()方法不仅执行了Reference的clear()方法(将弱引用置为空)同时还将entry置为空,这样就不会再引用myObject了。
这样的目的是,如果ThreadLocal这个对象被回收后,那么这个原来指向ThreadLocal对象的Entry就会指向null。

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

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

对象的生命周期

Thread退出时,ThreadLocalMap对象会被回收。但是在使用线程池时,线程不会退出,如果使用了ThreadLocal而没有执行ThreadLocal.remove()方法的话可能会导致内存泄漏。

你可能感兴趣的:(JAVA基础)