ThreadLocal
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一乐ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题,如下图所示
ThreadLocal实现原理
因为一个线程内可以存在多个 ThreadLocal 对象,所以其实是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫做 ThreadLocalMap 的静态内部类。而我们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。
ThreadLocalMap本身就是一个Map,其中Map的key是ThreadLocal对象,而value则是ThreadLocal中存储的对象。除此之外,Map的key继承了WeakRefence,也就是说ThreadLocalMap的key是一个弱依赖,如果GC Root不可达的情况下,在下一次Java GC时会被回收
set方法源码
public void set(T value) { //(1)获取当前线程(调用者线程) Thread t = Thread.currentThread(); //(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //(3)如果map不为null,就直接添加本地变量,key为当前定义的ThreadLocal变量的this引用,值为添加的本地变量值 if (map != null) map.set(this, value); //(4)如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); }
get方法源码
public T get() { //(1)获取当前线程 Thread t = Thread.currentThread(); //(2)获取当前线程的threadLocals变量 ThreadLocalMap map = getMap(t); //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量 return setInitialValue(); } private T setInitialValue() { //protected T initialValue() {return null;} T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 if (map != null) map.set(this, value); //如果map为null,说明首次添加,需要首先创建出对应的map else createMap(t, value); return value; }
在get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。
remove方法源码
public void remove() { //获取当前线程绑定的threadLocals ThreadLocalMap m = getMap(Thread.currentThread()); //如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量 if (m != null) m.remove(this); }
remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量
ThreadLocal例子
package test; public class ThreadLocalTest { static ThreadLocal<String> localVar = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + localVar.get()); //清除本地内存中的本地变量 localVar.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 localVar.set("localVar1"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + localVar.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 localVar.set("localVar2"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + localVar.get()); } }); t1.start(); t2.start(); } }
- 首先我们需要创建一个线程共享的ThreadLocal对象,该对象用于存储Integer类型的值;
- 然后在每条线程中可以通过如下方法操作ThreadLocal:
set(obj)
:向当前线程中存储数据get()
:获取当前线程中的数据remove()
:删除当前线程中的数据