ThreadLocal 详解
发布时间:2020-12-15 07:44:17 所属栏目:Java 来源:网络整理
导读:package java.lang; 简介 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个自己独立的变量副本。对于同一个ThreadLocal,每个线程通过get、set、remove接口操作只会影响自身线程的数据,不会干扰其他线程中的数据。使用场景:在每个线程希望
简介ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个自己独立的变量副本。 对于同一个ThreadLocal,每个线程通过get、set、remove接口操作只会影响自身线程的数据,不会干扰其他线程中的数据。 使用场景:在每个线程希望有一个独有的变量时,解决线程间隔离与线程内共享的问题 内存泄漏ThreadLocal内存泄漏的根源是: 由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key的value就会导致内存泄漏。 弱引用作用: ThreadLocal被Entry中的Key弱引用,在没有强引用的情况下ThreadLocal会被回收,应此key变成null。 对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除,减少了内存泄漏的概率。 ThreadLocal以一个弱引用身份被Entry中的Key引用的(static class Entry extends WeakReference<ThreadLocal<?>>): 因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。 这个时候Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。 因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来, 这些null key就存在一条强引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry-->Value, 这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。 JVM团队已经考虑到这样的情况,并做了一些措施来保证ThreadLocal尽量不会内存泄漏: 在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value(expungeStaleEntry(int staleSlot)), 并将整个Entry设置为null,利于下次内存回收。 如果数据初始化好之后,一直不调用get、set等方法,这样Entry就一直不能回收,导致内存泄漏。所以一旦数据不使用最好主动remove()。 示例public class Jdbc { //创建一个存储数据库连接对象的ThreadLocal线程本地变量 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); /** * 获取数据库的连接对象 */ public static Connection getConnection() { Connection conn = null; conn = tl.get(); if (conn == null) { try { conn = DriverManager.getConnection(); //将连接对象放入对应的ThreadLocal中(绑定到使用它的线程对象上) tl.set(conn); } catch (SQLException e) { e.printStackTrace(); } } return conn; } /** * 关闭数据库的连接,并删除对应的ThreadLocal中的对象 */ public static void closeConnection() { Connection conn = null; conn = tl.get(); if (conn != null) { tl.remove(); try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } 源码分析public class ThreadLocal<T> { protected T initialValue() { return null; } public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { return new SuppliedThreadLocal<>(supplier); } public ThreadLocal() { } //ThreadLocalMap以当前的threadLocal对象为key,get()方法时通过当前threadLocal实例就可以找到绑定在当前线程上的副本对象。 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(); } 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); } if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; } public void set(T value) { Thread t = Thread.currentThread(); //获取当前线程的Map ThreadLocalMap map = getMap(t); //如果map不为空,以当前threadLocal对象为key,实际存储对象为value进行set操作 if (map != null) { map.set(this,value); } else { //如果map为空,则创建ThreadLocalMap createMap(t,value); } } //每个Thread线程中都封装了一个ThreadLocalMap对象 void createMap(Thread t,T firstValue) { t.threadLocals = new ThreadLocalMap(this,firstValue); } ThreadLocalMap getMap(Thread t) { //"ThreadLocal.ThreadLocalMap threadLocals = null;"封装在Thread类中,每个线程都持有一个ThreadLocalMap变量 return t.threadLocals; } public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } } static class ThreadLocalMap { /** * Entry继承自WeakReference * WeakReference(弱引用)的特性: * 从GCRoots出发的引用中没有有效引用指向该对象,则该对象就可以被回收。 * 这里的有效引用并不包含WeakReference(弱引用是不可达对象引用),所以弱引用不影响对象被GC。 */ static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k,Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table; private int size = 0; private int threshold; // Default to 0 ThreadLocalMap(ThreadLocal<?> firstKey,Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey,firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key,i,e); } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |