ThreadLocal分析

含义

线程本地变量,线程内部进行数据传递,即每个线程对ThreadLocal的读写操作是线程隔离的,互不影响的

实现原理

  •  Thread 类内部有个 ThreadLocal.ThreadLocalMap 的变量,即每个线程都维护一个自己的ThreadLocalMap,目的就是实现线程隔离
public class Thread implements Runnable{
      ThreadLocal.ThreadLocalMap threadLocals = null;
}
  • 当调用ThreadLocal的set方法时,实际是向当前线程(Thread)类的ThreadLocalMap的中添加元素,其中ThreadLocalMap中的Entry是的key是Threadloacal的弱引用
    
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);
    }
}
static class Entry extends WeakReference<ThreadLocal<?>>{
     Object value;

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

 private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //设置key,value放入map中
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
  • 同样读也是从当前线程中读取

为什么使用弱引用

  •      首先需明白强引用,软引用,弱引用,虚引用的
  •      弱引用主要是防止内存泄漏,如果没有弱引用,只要线程还存在,即使程序没有了对ThreadLocal的强引用,当时线程内部的 ThreadLocalMap 引用依旧存在,那么key的强引用也依旧存在,那么key就无法被回收,如此就会造成key的内存泄漏,进而是 value的泄漏,但是如果key是弱引用,那么当垃圾回收时,只要ThreadLocal强引用没有了,key就可以被回收,这样就不会造成内存泄漏了。

父子线程

  • InheritableThreadLocal提供一种父子线程之间数据共享功能
  • 具体实现是在Thread类中除了threadLocals外还有一个inheritableThreadLocals对象。在线程对象初始化的时候,会调用ThreadLocal的createInheritedMap从父线程的inheritableThreadLocals中把有效的entry拷过来                                                                      
    private Thread(ThreadGroup g, Runnable target, String name,
                   long stackSize, AccessControlContext acc,
                   boolean inheritThreadLocals) {
        //省略...
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        //...
    }
  • 需要注意的是InheritableThreadLocal只是在子线程创建的时候会去拷一份父线程的inheritableThreadLocals。如果父线程是在子线程创建后再set某个InheritableThreadLocal对象的值,对子线程是不可见的。

应用场景

注意事项:

  • 使用完 ThreadLocal ,最好手动调用 remove() 方法,否则可能出现内存泄漏问题(原因就是ThreadLocalMap的key 是ThreadLocal 的弱引用,若key 被回收了,但是value却可以一直存在,对应null的key,这样value就无法被回收了造成内存泄漏)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值