主要是对博客的一些自己的理解和补充 地址:http://www.cnblogs.com/-new/p/7604420.html
概述:
这里套用该博主的实验代码:
public class T { ThreadLocal<Long> longLocal = new ThreadLocal<Long>(); public void set() { longLocal.set(Thread.currentThread().getId()); } public long getLong() { return longLocal.get(); } public static void main(String[] args) throws InterruptedException { final T test = new T(); test.set(); Thread thread1 = new Thread(){ public void run() { test.set(); System.out.println("线程一:"+test.getLong()); }; }; thread1.start(); thread1.join(); System.out.println("main线程:"+test.getLong()); System.out.println("没有发生值的覆盖,两个线程保存的值是不同的"); } }
1、基本分析
- 对于主线程和thread1两个线程来说,他们共享了T类的test这个对象,这个时候两者可以通过ThreadLocal类来分别保存一个test对象里面的值;也就是说:test对象不变,但是里面有一个变量是因使用test这个对象不同而不同的,longLocal变量就是这个变量
- 它的底层是使用一个ThreadLocalMap来实现的(每一个Thread里面都有这个对象),里面的Entry键名为某一个对象的ThreadLocal的实例,键值为具体的值,一个线程可以绑定多个ThreadLocal对象; 并且,Thread对象的成员ThreadLocalMap是懒加载的,用到的时候才加载:(注意:这里的threadLocals是线程t的一个成员变量ThreadLocalMap的实例)
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
这里是使用ThreadLocal对象的实例作为Entry的键名
/** * Construct a new map initially containing (firstKey, firstValue). * ThreadLocalMaps are constructed lazily, so we only create * one when we have at least one entry to put in it. */ 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); }
这里是set方法的源码,使用的是开地址法
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ 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; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
2、可能存在的问题:内存泄漏
注意:由于ThreadLocal.ThreadLocalMap.Entry继承自弱连接,当强连接断了之后,下一次GC就可以释放键名ThreadLocal对象的内存。 但是,value:在ThreadLocal.ThreadLocalMap.Entry中使用的强连接,无法由程序自动GC,必须通过remove、set、get等方法触发GC
/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
ps:第一次写博客,写的也只是自己的一些心得体会,有什么不足之处欢迎指出
原文地址:https://www.cnblogs.com/douglasdoudou/p/8849147.html
时间: 2024-10-03 00:32:42