ThreadLocal
来源:互联网 发布:python哪个版本好用 编辑:程序博客网 时间:2024/05/22 12:21
首先Thread类中持有了ThreadLocalMap类型的对象;
ThreadLocal是负责向当前线程的添加数据到ThreadLocalMap 或 从 ThreadLocalMap中取的数据;
TheadLocal类的方法:set(T value) ,get().,remove()
set源码
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; }
可以看出是从当前线程中获得 ThreadLocal.ThreadLocalMap对象;
如果map不为空则设置值,如果为空则创建map对象并设置值;
1.先来看创建map:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
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 final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
private static AtomicInteger nextHashCode = new AtomicInteger();
nextHashCode是静态量,并且从0开始增加,因此每次new ThreadLocal对象,都会有新的threadLocalHashCode值;
构造函数中初始化了table变量,用来存储Entry数据的数组;
Entry是实现了WeakReference的类,用来存储ThreadLocal对象以及value(自己要存储的数据);
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);这一句是 计算 数据存储在table数组中的位置; 意义就是对:数组的容量进行取余运算;
数组的容量一定要是2^n,这样用 (hashcode)&(2^n-1)来计算,正好就是取余运算(即取hashcode的低n-1位数代表位置);
关与HASH_INREMENT =0x61c88647; 是为了减少hash冲突,使数据在table中分布更均匀;
2.再来看map的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; //通过当前ThreadLocal对象的hashcode来获得存储位置 int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); //判断当前位置中存储的ThreadLocal是否是同一个对象,如果是则覆盖value值;如果k不为空,且又不是同一个对象, //则要通过nextIndex方法找下一个位置; if (k == key) { e.value = value; return; } //因为ThreadLocal对象有可能被回收,导致k==null,因此调用replaceStaleEntry if (k == null) { replaceStaleEntry(key, value, i); return; } } //没有匹配的,就在i位置赋值一个新的对象; tab[i] = new Entry(key, value); int sz = ++size; //调用cleanSomeSlots()对table进行清理,如果没有任何Entry被清理,并且表的size超过了阈值,就会调用rehash()方法。 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
//这是替换陈旧的entry(entry中的ThreadLocal对象为空的Entry);
* As a side effect, this method expunges all stale entries in the
* "run" containing the stale entry. (A run is a sequence of entries
* between two null slots.)
意思是删除所有的在两个Null位置之间的陈旧Entry
private void replaceStaleEntry(ThreadLocal key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; // Back up to check for prior stale entry in current run. // We clean out whole runs at a time to avoid continual // incremental rehashing due to garbage collector freeing // up refs in bunches (i.e., whenever the collector runs). int slotToExpunge = staleSlot; //找到紧挨着的上一个Entry不为空,且ThreadLocal引用为空的位置赋值给slotToExpunge for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; // Find either the key or trailing null slot of run, whichever // occurs first //从下一个位置开始找Entry,如果是一样的ThreadLocal,则 staleSlot位置换成当前位置的Entry对象, //当前位置换成staleSlot对象;下一步就是删除过时的Entry对象; for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // If we find key, then we need to swap it // with the stale entry to maintain hash table order. // The newly stale slot, or any other stale slot // encountered above it, can then be sent to expungeStaleEntry // to remove or rehash all of the other entries in run. if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists if (slotToExpunge == staleSlot) slotToExpunge = i; cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } // If we didn't find stale entry on backward scan, the // first stale entry seen while scanning for key is the // first still present in the run. if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // If key not found, put new entry in stale slot tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }
ThreadLocal的get()方法:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
尝试去获得当前线程中的map,如果map不为空就从map中找到对应的值并且返回;
如果没有存放ThreadLocal对象,则调用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); return value; }
protected T initialValue() { return null; }
initialValue()方法是需要我们实现的方法,通过它获得设置值;
下边就是向当前线程的ThreadLocalMap中设置值了;
remove方法删除当前线程ThreadLocalMap中与ThreadLocal键值对应的值;
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
总结:
ThreadLocal类是给线程中ThreadLocalMap设置值的, 如果你想设置多个值,就要new多个ThreadLocal对象给线程设置值;
另外要注意
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- ThreadLocal
- threadlocal
- ThreadLocal
- ThreadLocal
- LeetCode134. Gas Station
- 指针
- Notification&&自定义Notification
- android 开发工具下载 集合网站
- 数据库范式(1NF 2NF 3NF BCNF)详解
- ThreadLocal
- 正则表达式入门
- xml布局方式播放帧动画
- Python图像处理库PIL的Image模块介绍(四)
- lintcode:Single Number
- 实现nginx随机直接输出字符
- android筛选菜单实现
- 【NOIP2014TG】生活大爆炸版石头剪刀布
- [Python入门]Chapter3 函数