Java线程变量ThreadLocal详细解读
作者:小白不很白
Java线程变量ThreadLocal
多线程访问同一个变量的时候,很容易出现问题,特别是多线程对一个共享变量进行写入的时候。为了线程的安全在进行数据写入时候会进行数据的同步。
如上图: 如果要想实现多个线程操作同一个数据而不被其他线程所影响,就必须加锁操作,加锁又增加了使用的复杂度。
ThredLocal的用处就是当多个线程操作同一个变量的时候可以在创建一个ThredLocal,把共享变量复制到线程的本地threadLocals(threadLocals是Thread的成员变量) 中,这样各个线程进行操作的时候只能操作自己本地数据,这样就不会存储在线程不安全的问题了。
下面举个例子:
public class ThreadLocalTest { // 创建一个ThreadLocal static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); static Integer num = 5; public static void main(String[] args) throws InterruptedException { new Thread(()->{ // 线程1 把num 同步到threadLocal中 threadLocal.set(num); // 线程1 获取threadLocal中的数据 System.out.println("线程1: " + threadLocal.get()); }).start(); new Thread(()->{ // 线程2 获取threadLocal中的数据 System.out.println("线程2: "+ threadLocal.get()); }).start(); System.out.println("主线程: " + threadLocal.get()); } }
结果:由上面的返回可以知道,当在线程1设置了设置了数据时在线程2是访问不到的**,主线程**也访问不到。
ThreadLocal的实现原理
由下图可知 Thread类中有一个成员变量 ThreadLocal.ThreadLocalMap **threadLocals **= **null ** 是ThreadLocalMap类型的,本地变量都是存储在ThreadLocalMap中的,ThreadLocal只是一个外壳。ThreadLocalMap是ThreadLocal中的内部类,是一个定制化的Map形式,那就代表一个线程可以有多个ThreadLocal本地变量。key就是ThreadLocal本身,value就是存储的本地变量。
1.set()
public void set(T value) { // 获取当前线程 Thread t = Thread.currentThread(); // 获取线程的成员变量threadLocals ThreadLocalMap map = getMap(t); if (map != null) // ThreadLoacl 对象为key 同步的变量为value map.set(this, value); else // 如果是第一次 则创建ThreadLocalMap 并且赋值 createMap(t, value); } // 获取线程的成员变量threadLocals ThreadLocalMap getMap(Thread t) { return t.threadLocals; } // 第一次创建ThreadLocalMap(this, firstValue) void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
2.get()
public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取线程的成员变量threadLocals ThreadLocalMap map = getMap(t); if (map != null) { // 获取hashMap的值 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 如果为null return setInitialValue(); } private T setInitialValue() { // 初始化值为null T value = initialValue(); // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的threadLocals 成员变量 ThreadLocalMap map = getMap(t); // 如果数据为null则会存储把这个ThreadLocal的对象在这个线程的值存储为null if (map != null) map.set(this, value); else createMap(t, value); return value; } //初始化值 protected T initialValue() { return null; }
3. remove()
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
ThreadLocal不具备继承性
从上面的set接口 和 get接口就不难看出我们现在存储、获取、删除数据都是在当前线程threadLocals成员变量中存储的。所以父线程和存储的数据在子线程中获取不了,在子线程中存储的数据在父线程中存储不了。
例:
public class ThreadLocalTest { static ThreadLocal<Integer> stringThreadLocal = new ThreadLocal<>(); static Integer num = 5; public static void main(String[] args) throws InterruptedException { new Thread(()->{ stringThreadLocal.set(num); System.out.println("线程1: " + stringThreadLocal.get()); new Thread(()->{ System.out.println("线程1的子线程: " + stringThreadLocal.get()); }).start(); }).start(); } }
结果:
InheritableThreadLocal类
为了解决上述问题,为了让子类能够访问父类的数据,则InheritableThreadLocal应用而生。
首先InheritableThreadLocal类继承了ThreadLocal类 并重写了ThreadLocla的三个方法
public class InheritableThreadLocal<T> extends ThreadLocal<T> { // 只返回父类线程的值 protected T childValue(T parentValue) { return parentValue; } // 获取线程inheritableThreadLocals值 ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } // 创建当前线程的inheritableThreadLocals void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
再看下Thread创建的init方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { ...... // 获取父类线程 Thread parent = currentThread(); ........ // 如果 在创建子类线程的时候如果返现父类线程的数据不为null 则把父类线程的数据存储到子类线程的 //inheritableThreadLocals成员变量中去 if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); }
这样父类线程的inheritThreadLocals数据就存储到了子类变量中去了。 值得注意的事,虽然能用父类的放进去的值,但是不影响父类的操作,也就是子类和父类的值是隔离开的。
package com.example.spring; public class ThreadLocalTest { static ThreadLocal<Integer> stringThreadLocal = new ThreadLocal<>(); static InheritableThreadLocal<Integer> integerInheritableThreadLocal = new InheritableThreadLocal(); static Integer num = 5; public static void main(String[] args) throws InterruptedException { new Thread(()->{ integerInheritableThreadLocal.set(num); System.out.println("线程1: " + integerInheritableThreadLocal.get()); new Thread(()->{ integerInheritableThreadLocal.set(4); System.out.println("线程1的子线程: " + integerInheritableThreadLocal.get()); }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1的最终结果:" + integerInheritableThreadLocal.get()); }).start(); } }
到此这篇关于Java线程变量ThreadLocal详细解读的文章就介绍到这了,更多相关Java线程变量ThreadLocal内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!