编程语言
首页 > 编程语言> > 从源码来看ThreadLocal

从源码来看ThreadLocal

作者:互联网

从源码来看ThreadLocal

  1. 为什么threadLocal线程安全

        public T get() {
            // 从下面2行代码可以看出不管是在哪个线程中运行,它拿出的ThreadLocalMap都是当前线程上的threadLocals的对象
            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();
        }
    
    
    ​	
    	ThreadLocalMap getMap(Thread t) {
    	    return t.threadLocals;
    	}
    
    
    
    
  2. 为什么要慎用threadLocal,发生内存泄漏的真正原因

    static class ThreadLocalMap {
    
         	// ThreadLocalMap内的Entry是WeakReference的子类
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
        
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
    
    
    

java 中的4种引用

名称描述生命周期
强引用普通的对象都是强引用宁愿OOM也不回收
软引用(SoftReference)一般用于系统内的高速缓存当内存不足的时候会回收
弱引用(WeakReference)这个用的很少,threadLocal中的Entry使用到了它当gc的时候会回收,不管内存还够不够
虚引用(PhantomReference)一般用来跟踪对象的生命周期,基本不会使用到未知
  1. 怎么访问其他线程threadLocal中的数据

    Thread.java
    	// 这个变量用来保障线程安全问题
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
        // 这个变量用来实现父子线程中的threadLocal通讯
        ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
    
     private void init(ThreadGroup g, Runnable target, String name,
                          long stackSize, AccessControlContext acc,
                          boolean inheritThreadLocals) {
           // 省略很多代码
            if (inheritThreadLocals && parent.inheritableThreadLocals != null)
                // 从这里看出来父线程中的inheritableThreadLocals会设置到子线程的inheritableThreadLocals上
                this.inheritableThreadLocals =
                    ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
            // 省略很多代码
        }
    
  2. threadLocal的数据结构

    threadLocal的底层是通过一个叫做threadLocalMap的数据结构实现的,但是threadLocalMap并没有实现Map接口,也没有使用链表,它里面用的是一个叫做Entry的数组

    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;
                }
            }
    
            private static final int INITIAL_CAPACITY = 16;
    
        	// threadLocal中的数据都存放在table这个数组中
            private Entry[] table;
    
    
    
    
    

    当threadLocalMap发生hash冲突的时候又是怎么解决的

    ThreadLocal.ThreadLocalMap 
        
    private Entry getEntry(ThreadLocal<?> key) {
        		// 用当前threadLocal的hashCode 和储存数组的长度进行&
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
        		// 当存在这个值并且2个key值相等的时候返回找到的值
                if (e != null && e.get() == key)
                    return e;
                else
                    // 当找不到或者发生hash冲突的时候就需要下面这个方法去解决了
                    return getEntryAfterMiss(key, i, e);
            }
    
    
     private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
                Entry[] tab = table;
                int len = tab.length;
    
                while (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == key)
                        return e;
                    if (k == null)
                        // 当key 是null的时候清理掉当前entry上存储的值,避免出现内存泄漏
                        expungeStaleEntry(i);
                    else
                        // 当前的坐标找不到,那么就继续往下找
                        // 从这里可以看出当hash冲突严重的时候threadLocal的效率会大幅度下降
                        i = nextIndex(i, len);
                    e = tab[i];
                }
                return null;
            }
    
  3. 正确使用ThreadLocal的姿势

    1. 初始化的时候使用static修饰
    private static ThreadLocal<String> th = new ThreadLocal<String>();
    
    2. 在业务代码中使用ThreadLocal
        
        th.set('aaaa');
    
    	try{
            // 业务代码。。。。
        }finally{
            // 虽然说每次在set和get的时候threadLocal都会情况key为null的值,但是由于threadLocal的生命周期和thread一样,当使用线程池的情况下,如果我们有一个对象使用用了之后一直没有移除,并且也没有在该线程内再次get set 那么,这个对象占用一块内存空间将一直得不到释放,从而造成内存泄漏
            th.remove();
        }
        
    
    

    这篇博客讲的很6,强烈建议阅读

标签:来看,ThreadLocalMap,threadLocal,ThreadLocal,源码,线程,Entry,null
来源: https://blog.csdn.net/sssdal19995/article/details/111225207