JDK8源码阅读:ThreadLocal

1.是什么

只要线程处于活动状态且ThreadLocal实例可访问,每个线程都拥有对其线程本地变量副本的隐式引用; 一个线程消失后,它的所有线程局部实例副本都要进行垃圾收集(除非存在对这些副本的其他引用)

2.源码解读

2.1.给ThreadLocal设置值

ThreadLocal的set方法,其实是首先获取当前线程,然后通过当前线程获取ThreadLocal的内部类ThreadLocalMap,然后在设置ThreadLocalMap的K=this(也就是设置K为ThreadLocal的实例),V=value

public class ThreadLocal<T> {

	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;
	}
	
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
}
public class Thread implements Runnable {
	ThreadLocal.ThreadLocalMap threadLocals = null;
}
2.2.从ThreadLocal获取值

首先获取当前线程Thread,然后获取当前线程的缺省属性ThreadlocalMap,然后查找ThreadLocalMap中K为ThreadLocal实例的Entry,最后返回value。如果ThreadLocalMap为null或者Entry不存在,则通过protected T initialValue()方法来设置初始值,并进行set方法操作。

protected T initialValue():该方法可以在实现时进行重写,这样可以在ThreadLocal的get方法没有获取到值时,返回initialValue()方法中的返回值

public class ThreadLocal<T> {
	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();
	}

    protected T initialValue() {
        return 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;
    }
}
2.3.从ThreadLocal中移除值

获取当前线程的缺省属性ThreadLocalMap,然后移除ThreadLocalMap中K为ThreadLocal实例的Entry

public class ThreadLocal<T> {
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
}
2.4.ThreadLocal的内存泄漏问题
	// static class ThreadLocal.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;
        }
    }

通过源码,可以分析出Entry的key是一个弱引用(如果一个对象只有弱引用指向它时,则这个对象会在虚拟机进行垃圾回收时被回收),所以若没有对象进行强引用到ThreadLocal实例时,则Entry中的key会被垃圾回收变为null,造成当前线程的ThreadLocalMap强引用Entry,但是无法获取到value的值,造成内存泄漏。所以JDK建议将ThreadLocal设置为private static属性,这样避免被垃圾收回。引用关系如下图所示:
这里写图片描述

实际使用demo

package com.zkane.test;

/**
 * @author 594781919@qq.com
 * @date 2018/8/22
 */
public class ThreadLocalTest {
    private static ThreadLocal<Long> threadLocalLong = new ThreadLocal(){
        @Override
        protected Long initialValue() {
            return 11L;
        }
    };
    private static ThreadLocal<String> threadLocalString = new ThreadLocal<>();

    public static void main(String[] args) {
        System.out.println("==" + threadLocalLong.get());
        threadLocalLong.set(Thread.currentThread().getId());
        threadLocalString.set(Thread.currentThread().getName());
        new Thread(() -> {
            threadLocalLong.set(Thread.currentThread().getId());
            threadLocalString.set(Thread.currentThread().getName());
            System.out.println("--->" + threadLocalLong);
            System.out.println("--->" + threadLocalLong.get());
            System.out.println("--->" + threadLocalString);
            System.out.println("--->" + threadLocalString.get());
        }).start();
        Thread.yield();
        System.out.println(threadLocalLong);
        System.out.println(threadLocalLong.get());
        System.out.println(threadLocalString);
        System.out.println(threadLocalString.get());

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值