第一步:分析 ThreadLocal的源码
ThreadLocal有一个内部类ThreadLocalMap,这个类的实现占了整个ThreadLocal类源码的一多半。这个ThreadLocalMap的作用非常关键,它就是线程真正保存线程自己本地变量的容器。每一个线程都有自己的单独的一个ThreadLocalMap实例,
public T get() { //获取当前执行线程 Thread t = Thread.currentThread(); //取得当前线程的ThreadLocalMap实例 ThreadLocalMap map = getMap(t); //如果map不为空,说明该线程已经有了一个ThreadLocalMap实例 if (map != null) { //map中保存线程的所有的线程本地变量,我们要去查找当前线程本地变量 ThreadLocalMap.Entry e = map.getEntry(this); //如果当前线程本地变量存在这个map中,则返回其对应的值 if (e != null) return (T)e.value; } //如果map不存在或者map中不存在当前线程本地变量,返回初始值 return setInitialValue(); }
注意这里最后一步是调用了
InitialValue()方法来实现对本地变量的初始化Thread对象都有一个ThreadLocalMap类型的属性threadLocals,这个属性是专门用于保存自己所有的线程本地变量的。这个属性在线程对象初始化的时候为null。所以对一个线程对象第一次使用线程本地变量的时候,需要对这个threadLocals属性进行初始化操作。
getMap方法: //直接返回线程对象的threadLocals属性 ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
setInitialValue方法: private T setInitialValue() { //获取初始化值,initialValue 就是我们之前覆盖的方法 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //如果map不为空,将初始化值放入到当前线程的ThreadLocalMap对象中 if (map != null) map.set(this, value); else //当前线程第一次使用本地线程变量,需要对map进行初始化工作 createMap(t, value); //返回初始化值 return value; }
我们再来看一下set方法 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); //说明线程第一次使用线程本地变量(注意这里的第一次含义) else createMap(t, value); } ThradLocal还有一个remove方法: public void remove() { //获取当前线程的ThreadLocalMap对象 ThreadLocalMap m = getMap(Thread.currentThread()); //如果map不为空,则删除该本地变量的值 if (m != null) m.remove(this); }
第二步:应用分析
package com.threadlocal; import java.util.logging.Logger; public class threadlocaldemo { private static Logger logger =Logger.getLogger("threadlocaldemo.class"); private static int id=0; private static ThreadLocal<People> threadLocal =new ThreadLocal<People>() { protected People initialValue() { People people =new People(); people.setId(++id); return people; }; }; public static void main(String[] args) { logger.info("测试开始"); for(int i=0;i<5;i++) { new Thread(new Runnable() { public void run() { System.out.println("my name is :"+Thread.currentThread().getName()+"--"+threadLocal.get().getId()); }; }).start(); } } }结果:
五月 03, 2018 10:55:15 上午 com.threadlocal.threadlocaldemo main 信息: 测试开始 my name is :Thread-3--3 my name is :Thread-5--2 my name is :Thread-4--1 my name is :Thread-2--1 my name is :Thread-1--1
从结果可以清晰的看出 并没有实现线程间变量相互隔离的效果,分析
private static int id=0; private static ThreadLocal<People> threadLocal =new ThreadLocal<People>() { protected People initialValue() { People people =new People(); people.setId(++id); return people; }; };
这种情况下的模型
情况二: package com.threadlocal; import java.util.logging.Logger; public class threadlocaldemo { private static Logger logger =Logger.getLogger("threadlocaldemo.class"); private static ThreadLocal<People> threadLocal =new ThreadLocal<People>() { protected People initialValue() { int id=0; People people =new People(); people.setId(++id); return people; }; }; public static void main(String[] args) { logger.info("测试开始"); for(int i=0;i<5;i++) { new Thread(new Runnable() { public void run() { System.out.println("my name is :"+Thread.currentThread().getName()+"--"+threadLocal.get().getId()); }; }).start(); } } }
结果:
信息: 测试开始 my name is :Thread-5--1 my name is :Thread-1--1 my name is :Thread-3--1 my name is :Thread-4--1 my name is :Thread-2--1
模型: