不积小流无以成江海,不积跬步无以至千里一、概念
ThreadLocal 是一个关于创建线程局部变量的类,主要作用是做数据隔离,保存到 ThreadLocal 中的数据只属于当前线程,该数据对其他线程而言是隔离的。即使用 ThreadLocal 保存的数据只能被当前线程访问,其他线程无法访问和修改。在多线程环境下,防止自己的变量被其他线程篡改。
注意:ThreadLocal 设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题。1.1 场景
public class Main { private static ThreadLocalthreadLocal = new ThreadLocal<>(); public static void main(String[] args) { // 主线程设置值 threadLocal.set("value1"); System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get()); // 子线程 new Thread(new Runnable() { @Override public void run() { // 子线程获取的值是:null System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get()); } }).start(); } }
场景1:主线程初始化了一个 ThreadLocal 对象 threadLocal,并通过 threadLocal.set() 方法保存了一个值:“value1”,然后使用 threadLocal.get() 拿到设置的值。其中,子线程也使用 threadLocal.get() 去拿值。
问题:取值为null;
public class Main { private static ThreadLocalthreadLocal = new ThreadLocal<>(); public static void main(String[] args) { // 主线程设置值 User user = new User(); threadLocal.set(user); System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get()); // 子线程 new Thread(new Runnable() { @Override public void run() { threadLocal.set(user); // 获取到的对象地址是一样的,如果对它的值进行了修改,那么其他线程拿到的值也会改变。 System.out.println(Thread.currentThread().getName() + " = " + threadLocal.get()); } }).start(); } }
场景2:把变量换成是一个共享的对象保存到 ThreadLocal 中,那么多个线程的 ThreadLocal.get() 取得的还是这个共享对象本身;
问题:还是有并发访问问题。
三、源码分析 3.1 Set方法public void set(T value) { // 获取当前线程 Thread t = Thread.currentThread(); // 利用当前线程获取一个 ThreadLocalMap 的对象 ThreadLocalMap map = getMap(t); // 如果上面获取的 ThreadLocalMap 对象不为空,则设置值,否则创建这个 ThreadLocalMap 对象并设置值 if (map != null) map.set(this, value); else createMap(t, value); } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
从 set 源码中,我们得知 ThreadLocalMap 是利用当前线程 Thread 作为参数获取的。源码如下:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
其实,上面的代码获取的是 Thread 对象的 threadLocals 变量。源码如下:
public class Thread implements Runnable { 省略其他内容... ThreadLocal.ThreadLocalMap threadLocals = null; 省略其他内容... }
结论:可以知道 ThreadLocal 的数据是放入了当前线程的一个ThreadLocalMap 实例中,key 就是 ThreadLocal 对象本身,所以只能在本线程中访问,其他线程无法访问,从而实现了数据隔离。
3.2 get方法public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取 ThreadLocalMap 对象 ThreadLocalMap map = getMap(t); if (map != null) { // 以 ThreadLocal 对象本身作为 key,获取值 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 如果 ThreadLocalMap 对象不存在,就设置初始值并返回 // 从下面 setInitialValue() 的源码可知,设置的初始值是一个 null 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); return value; } protected T initialValue() { return null; }
主要工作原理
- Thread 类中维护着一个 ThreadLocalMap 类型的成员变量。
- ThreadLocalMap 是一个定义在 ThreadLocal 类中的内部类,是一个 map,用Entry 来进行数据存储。
- 当调用 ThreadLocal 的 set() 方法时,先获取当前线程的 ThreadLocalMap 对象,然后以 ThreadLocal 对象作为 key 往ThreadLocalMap 中设置值。
- 当调用 ThreadLocal 的 get() 方法时,也是先获取当前线程的 ThreadLocalMap 对象,以 ThreadLocal 对象作为 key 从 ThreadLocalMap 中获取值。
- ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程在ThreadLocalMap 中获取或设置值。
参考
Star Zheng 《聊一聊Java中的ThreadLocal》
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)