ThreadLocal

2019-02-28  本文已影响0人  lionel880

阅读此文前,请先阅读WeakReference和WeakHashMap

一、ThreadLocal的基本使用

ThreadLocal是一个对象,这个对象是当前线程绑定的,可以在线程的任何位置任何时刻取出

基本使用方式为:

public class ThreadLocalTest {
    ThreadLocal<String> threadLocal=new ThreadLocal<>();
    @Test
    public void testThreadLocal() throws InterruptedException {

        new Thread(){
            public void run(){
                //在线程的任意地方设置变量
                threadLocal.set("你");
                method();
            }
        }.start();
        new Thread(){
            public void run(){
                threadLocal.set("好");
                method();
            }
        }.start();
        Thread.sleep(3000);
    }
    public  void method(){
        //可以在当前线程的任意地方获取变量
        System.out.println(threadLocal.get());
    }
}

二、ThreadLocal的原理

首先对类的构成有个理解


image.png

ThreadLocal有一个静态内部类ThreadLocalMap,因为是静态内部类,意味着不依赖于外部类。
ThreadLocalMap内部的静态内部类Entry

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

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

ThreadLocalMap有点儿像WeakHashMap,但区别是,entry的构造方法
它并没有传入一个referenceQueue,我们知道,当使用reference.null,我们不会对referenceQueue进行enqueue操作。
也就是虽然会把referent置为null,但不和weakHashMap一样,通过queue.poll方法找到陈旧的数据

一步步来分析ThreadLocal的实现
1.set方法threadLocal.set("好");
获得map,我们可以看到调用了getMap方法,最终返回了当前线程的ThreadLocal.ThreadLocalMap,即每个线程调用set方法,都是在当前线程的map中进行操作的,map里会放入Entry<K,V>,对象的key是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);
    }
...
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

2.get方法,threadLocal.get()

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
...

三、ThreadLocal弱引用的用处

image.png

我们看看关于ThreadLocal里的相关引用

好了,现在可以分析为何要这样进行设计。
类比于WeakHashMap,它的put方法是put(K,V),可以是完全不相关的东西。
但LocalThread的设计,使得LocalThreadMap的Key其实是当前对象,这意味着什么。

假设一个场景,你主观上想回收你的ThreadLocal

ThreadLocal<String> tl=new ThreadLocal<>();
tl.set("abc");
....
tl=null;

四、这种设计下,ThreadLocal还可能会有内存泄露吗?

当ThreadLocalRef置为null后,young GC会把referent置为null,但此时value还存在。
ThreadLocal的expungeStaleEntry方法,会在remove,rehash等方法中调用,如果你不显式调用remove方法,意味着你只有在rehash的时候,才会进行轮询数组,清除无效数据,所以不使用remove时容易造成内存泄露

五、强引用,软引用,弱引用,虚引用的例子

package example.reference;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

/**
 * @author liuhaibo on 2018/03/06
 */
public class WeakRefDemo {

    public static void main(String... args) {

        // all these objects have a strong reference
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        // other references to these objects
        Object strongA = a;
        SoftReference<Object> softB = new SoftReference<>(b);
        WeakReference<Object> weakC = new WeakReference<>(c);

        // free the former strong references to these objects:

        // there is still a strong reference(strongA) to the first object
        a = null;
        // only a soft reference(softB) refers to the second object
        b = null;
        // only a weak reference(weakC) refers to the third object
        c = null;

        System.out.println("Before gc...");
        System.out.println(String.format("strongA = %s, softB = %s, weakC = %s", strongA, softB.get(), weakC.get()));

        System.out.println("Run GC...");

        System.gc();

        // object with only soft reference will be cleaned only if memory is not enough: 用来做缓存很不错
        // object with only weak reference will be cleaned after a gc operation:
        System.out.println("After gc...");
        System.out.println(String.format("strongA = %s, softB = %s, weakC = %s", strongA, softB.get(), weakC.get()));
    }
}

结果为

Before gc...
strongA = java.lang.Object@3af49f1c, softB = java.lang.Object@19469ea2, weakC = java.lang.Object@13221655
Run GC...
After gc...
strongA = java.lang.Object@3af49f1c, softB = java.lang.Object@19469ea2, weakC = null

上一篇 下一篇

猜你喜欢

热点阅读