赞
踩
方法名 | 描述 |
---|---|
ThreadLocal() | 创建ThreadLocal对象 |
public void set( T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
public T remove() | 移除当前线程绑定的局部变量,该方法可以帮助JVM进行GC |
protected T initialValue() | 返回当前线程局部变量的初始值 |
/** * 线程间访问共享变量之间问题 * */ public class DemoQuestion { private String name; private int age; public static void main(String[] args) { DemoQuestion demoQuestion = new DemoQuestion(); for (int i = 0; i < 5; i++) { // int j = i; new Thread(() ->{ // demoQuestion.setAge(j); demoQuestion.setName(Thread.currentThread().getName() + "的数据"); System.out.println("================="); System.out.println(Thread.currentThread().getName() + "--->" + demoQuestion.getName()); // System.out.println(Thread.currentThread().getName() + "--->" + demoQuestion.getAge()); },"t" + i).start(); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
/** * 使用加锁的方式解决:线程间访问共享变量之间问题 * 将对共享变量的操作进行加锁,保证其原子性 * */ public class SolveDemoQuestionBySynchronized { private String name; private int age; public static void main(String[] args) { SolveDemoQuestionBySynchronized demoQuestion = new SolveDemoQuestionBySynchronized(); for (int i = 0; i < 5; i++) { // int j = i; new Thread(() ->{ synchronized (SolveDemoQuestionBySynchronized.class){ demoQuestion.setName(Thread.currentThread().getName() + "的数据"); System.out.println("================="); System.out.println(Thread.currentThread().getName() + "--->" + demoQuestion.getName()); } },"t" + i).start(); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class SolveDemoQuestionByThreadLocal { private ThreadLocal<String> name = new ThreadLocal<>(); private int age; public static void main(String[] args) { SolveDemoQuestionByThreadLocal demoQuestion = new SolveDemoQuestionByThreadLocal(); for (int i = 0; i < 5; i++) { new Thread(() ->{ demoQuestion.setName(Thread.currentThread().getName() + "的数据"); System.out.println("================="); System.out.println(Thread.currentThread().getName() + "--->" + demoQuestion.getName()); },"t" + i).start(); } } public String getName() { return name.get(); } private void setName(String content) { name.set(content); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
/** * 设置当前线程对应的ThreadLocal的值 * @param value 将要保存在当前线程对应的ThreadLocal的值 */ public void set(T value) { // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 判断map是否存在 if (map != null) // 存在则调用map.set设置此实体entry,this这里指调用此方法的ThreadLocal对象 map.set(this, value); else // 1)当前线程Thread 不存在ThreadLocalMap对象 // 2)则调用createMap进行ThreadLocalMap对象的初始化 // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中 createMap(t, value); } /** * 获取当前线程Thread对应维护的ThreadLocalMap * * @param t the current thread 当前线程 * @return the map 对应维护的ThreadLocalMap */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** *创建当前线程Thread对应维护的ThreadLocalMap * @param t 当前线程 * @param firstValue 存放到map中第一个entry的值 */ void createMap(Thread t, T firstValue) { //这里的this是调用此方法的threadLocal t.threadLocals = new ThreadLocalMap(this, firstValue); }
/** * 返回当前线程中保存ThreadLocal的值 * 如果当前线程没有此ThreadLocal变量, * 则它会通过调用{@link #initialValue} 方法进行初始化值 * @return 返回当前线程对应此ThreadLocal的值 */ public T get() { // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 如果此map存在 if (map != null) { // 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体e ThreadLocalMap.Entry e = map.getEntry(this); // 对e进行判空 if (e != null) { @SuppressWarnings("unchecked") // 获取存储实体 e 对应的 value值,即为我们想要的当前线程对应此ThreadLocal的值 T result = (T)e.value; return result; } } /* 初始化 : 有两种情况有执行当前代码 第一种情况: map不存在,表示此线程没有维护的ThreadLocalMap对象 第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry */ return setInitialValue(); } /** * 初始化 * @return the initial value 初始化后的值 */ private T setInitialValue() { // 调用initialValue获取初始化的值 // 此方法可以被子类重写, 如果不重写默认返回null T value = initialValue(); // 获取当前线程对象 Thread t = Thread.currentThread(); // 获取此线程对象中维护的ThreadLocalMap对象 ThreadLocalMap map = getMap(t); // 判断map是否存在 if (map != null) // 存在则调用map.set设置此实体entry map.set(this, value); else // 1)当前线程Thread 不存在ThreadLocalMap对象 // 2)则调用createMap进行ThreadLocalMap对象的初始化 // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中 createMap(t, value); // 返回设置的值value return value; }
/**
* 删除当前线程中保存的ThreadLocal对应的实体entry
*/
public void remove() {
// 获取当前线程对象中维护的ThreadLocalMap对象
ThreadLocalMap m = getMap(Thread.currentThread());
// 如果此map存在
if (m != null)
// 存在则调用map.remove
// 以当前ThreadLocal为key删除对应的实体entry
m.remove(this);
}
/** * 返回当前线程对应的ThreadLocal的初始值 * 此方法的第一次调用发生在,当线程通过get方法访问此线程的ThreadLocal值时 * 除非线程先调用了set方法,在这种情况下,initialValue 才不会被这个线程调用。 * 通常情况下,每个线程最多调用一次这个方法。 * * <p>这个方法仅仅简单的返回null {@code null}; * 如果想ThreadLocal线程局部变量有一个除null以外的初始值, * 必须通过子类继承{@code ThreadLocal} 的方式去重写此方法 * 通常, 可以通过匿名内部类的方式实现 * * @return 当前ThreadLocal的初始值 */ protected T initialValue() { return null; }
/** * 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. * * Entry继承WeakReference,并且用ThreadLocal作为key. * 如果key为null(entry.get() == null),意味着key不再被引用, * 因此这时候entry也可以从table中清除。 */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } /** * 初始容量 —— 必须是2的整次幂 * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * 存放数据的table,Entry类的定义在下面分析 * 同样,数组长度必须是2的整次幂。 * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; /** * 数组里面entrys的个数,可以用于判断table当前使用量是否超过阈值。 * The number of entries in the table */ private int size = 0; /** * 进行扩容的阈值,表使用量大于它的时候进行扩容。 * The next size value at which to resize */ private int threshold; // Default to 0
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocal.ThreadLocalMap map = getMap(t); if (map != null) //调用了ThreadLocalMap的set方法 map.set(this, value); else createMap(t, value); } ThreadLocal.ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { //调用了ThreadLocalMap的构造方法 t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue); }
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)
/*
* firstKey : 本ThreadLocal实例(this)
* firstValue : 要保存的线程本地变量
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table
table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
//计算索引(重点代码)
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//设置值
table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
size = 1;
//设置阈值
setThreshold(INITIAL_CAPACITY);
}
构造函数首先创建一个长度为16的Entry数组,然后计算出firstKey对应的索引,然后存储到table中,并设置size和threshold
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
//AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减,适合高并发情况下的使用
private static AtomicInteger nextHashCode = new AtomicInteger();
//特殊的hash值
private static final int HASH_INCREMENT = 0x61c88647;
这里定义了一个AtomicInteger类型,每次获取当前值并加上HASH_INCREMENT,HASH_INCREMENT = 0x61c88647,这个值跟斐波那契数列(黄金分割数)有关,其主要目的就是为了让哈希码能均匀的分布在2的n次方的数组里, 也就是Entry[] table中,这样做可以尽量避免hash冲突
private void set(ThreadLocal<?> key, Object value) { ThreadLocal.ThreadLocalMap.Entry[] tab = table; int len = tab.length; //计算索引(重点代码,刚才分析过了) int i = key.threadLocalHashCode & (len-1); /** * 使用线性探测法查找元素(重点代码) */ for (ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); //ThreadLocal 对应的 key 存在,直接覆盖之前的值 if (k == key) { e.value = value; return; } // key为 null,但是值不为 null,说明之前的 ThreadLocal 对象已经被回收了, // 当前数组中的 Entry 是一个陈旧(stale)的元素 if (k == null) { //用新元素替换陈旧的元素,这个方法进行了不少的垃圾清理动作,防止内存泄漏 replaceStaleEntry(key, value, i); return; } } //ThreadLocal对应的key不存在并且没有找到陈旧的元素,则在空元素的位置创建一个新的Entry。 tab[i] = new Entry(key, value); int sz = ++size; /** * cleanSomeSlots用于清除那些e.get()==null的元素, * 这种数据key关联的对象已经被回收,所以这个Entry(table[index])可以被置null。 * 如果没有清除任何entry,并且当前使用量达到了负载因子所定义(长度的2/3),那么进行 * rehash(执行一次全表的扫描清理工作) */ if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } /** * 获取环形数组的下一个索引 */ private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
代码执行流程:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。