ThreadLocal是什么
ThreadLocal官方注释:
翻译过来大致意思是:ThreadLocal可以提供局部变量,通过set和get方法对局部变量进行操作,并且局部变量是每个线程独立的、数据隔离的。ThreadLocal通常作为线程的私有的静态变量,用于和UserId、事务Id相关联。
set方法:
public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); //如果ThreadLocalMap不是空则直接把当前ThreadLocal作为key存到map中 if (map != null) map.set(this, value); else //如果ThreadLocalMap是空,就初始化map createMap(t, value); }
getMap方法:
//获取ThreadLocalMap就是获取当前线程的threadLocals属性 ThreadLocal.ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
createMap方法:
void createMap(Thread t, T firstValue) { //新增一个ThreadLocalMap,把当前ThreadLocal作为key存到map中 //并且把新增的ThreadLocalMap作为当前线程的threadLocals属性 t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue); }
get方法:
public T get() { //获取当前线程 Thread t = Thread.currentThread(); //获取ThreadLocalMap ThreadLocal.ThreadLocalMap map = getMap(t); //如果ThreadLocalMap不为空就通过当前ThreadLocal对象获取Entry,返回Entry的值 if (map != null) { ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T) e.value; return result; } } //如果ThreadLocalMap是空的就进行初始化 return setInitialValue(); }
setInitialValue方法:
private T setInitialValue() { //初始化value,实际是null T value = initialValue(); //把刚初始化的null值set到ThreadLocalMap中 Thread t = Thread.currentThread(); ThreadLocal.ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); //返回value return value; }
initialValue方法:
protected T initialValue() { return null; }
总结:ThreadLocal是多线程用来保存局部变量的一个类,而且保存的变量是具有隔离性的,线程独立的!
ThreadLocalMap是什么
通过ThreadLocal的set和get方法可以看到一个非常重要的类:ThreadLocalMap。实际上ThreadLocal的set和get方法都是通过这个map在操作数据,并且多线程Thread类的保存变量的属性就是ThreadLocalMap,所以它是连接ThreadLocal和Thread的桥梁。
那么ThreadLocalMap到底是什么?
ThreadLocalMap是ThreadLocal的一个静态内部类,它内部还有一个Entry静态内部类,所以ThreadLocal对数据的操作实际上是ThreadLocalMap,而ThreadLocalMap的操作又是由Entry来完成的。
/** * Entry继承了弱引用,通常把ThreadLocal对象作为key。 * 注意:当entry.get()==null时说明这个key没有被引用了,是可以被回收的 */ static class Entry extends WeakReference<ThreadLocal<?>> { /** * 这个值是与ThreadLocal相关联的value */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
ThreadLocalMap的关键方法:
set方法
private void set(ThreadLocal<?> key, Object value) { //table就是内部维护的Entry数组,用来存数据 Entry[] tab = table; int len = tab.length; //根据ThreadLocal的哈希值和Entry数组的长度-1进行逻辑运算,算出数据下标 int i = key.threadLocalHashCode & (len - 1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); //如果key相同就直接替换value if (k == key) { e.value = value; return; } //如果e.get()为空则说明之前的Entry没有相关引用了(被称为StaleEntry),所以进行更新当前节点的Entry if (k == null) { replaceStaleEntry(key, value, i); return; } } //走到这说明在i的下标处没有数据,所以直接新增一个Entry tab[i] = new Entry(key, value); int sz = ++size; //cleanSomeSlots方法是清除被称为StaleEntry //如果cleanSomeSlots方法返回false即没有StaleEntry并且当前数据量大于阈值时进行rehash if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
get方法
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 //走到这有两种情况:1、Entry不同但是key相同,直接返回;2、key为null的stale节点,进行清除操作 return getEntryAfterMiss(key, i, e); }
remove方法
private void remove(ThreadLocal<?> key) { 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)]) { //先清除节点然后再调用清除staleEntry的方法防止内存泄漏 if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }
ThreadLocal的简单使用
多线程操作普通引用对象时会存在数据安全问题:
public class MyThread1 { static String str = "000"; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread1.str = String.valueOf(id); System.out.println("t1:" + MyThread1.str); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread1.str = String.valueOf(id); System.out.println("t2:" + MyThread1.str); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread1.str = String.valueOf(id); System.out.println("t3:" + MyThread1.str); } }); t1.start(); t2.start(); t3.start(); } }
结果:
t2:12 t3:13 t1:12
使用ThreadLocal来保证数据隔离:
public class MyThread2 { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread2.threadLocal.set(String.valueOf(id)); System.out.println("t1:" + MyThread2.threadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread2.threadLocal.set(String.valueOf(id)); System.out.println("t2:" + MyThread2.threadLocal.get()); } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { long id = Thread.currentThread().getId(); MyThread2.threadLocal.set(String.valueOf(id)); System.out.println("t3:" + MyThread2.threadLocal.get()); } }); t1.start(); t2.start(); t3.start(); } }
结果:
t1:11 t2:12 t3:13
Thread和ThreadLocal、ThreadLocalMap的关系
Thread和ThreadLocal:ThreadLocal保证每个Thread的数据是隔离的。
Thread和ThreadLocalMap:Thread类有个成员变量是ThreadLocalMap,并以ThreadLocal为key来存储数据。
ThreadLocal和ThreadLocalMap:ThreadLocalMap是ThreadLocal的内部类,实际上ThreadLocal对数据的操作是通过ThreadLocalMap进行的。
标签:Thread,ThreadLocalMap,value,ThreadLocal,key,Entry From: https://www.cnblogs.com/Bernard94/p/17994977