Thread类
有许多重载的构造器
new Thread(……)
调用构造器,在构造器中会调用init方法
在init方法中,调用currentThread()方法获得当前正在创建新线程的线程,并将新线程的daemon、priority、contextClassLoader、target、stackSize等设置为和创建新线程的线程一致。this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);(子线程继承父线程的相关设置)
在init方法中,并不会初始化Thread类的ThreadLocal.ThreadLocalMap类型的成员变量,而是在当前线程中,调用了ThreadLocal的get或者set方法时,才会初始化该属性。
在当前线程中,可以创建一个或者多个ThreadLocal类型的变量
public class TestThreadLocal2 { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); private static ThreadLocal<String> threadLocal2 = new ThreadLocal<String>(); @Test public void test(){ for(int i=0;i<5;i++){ new Thread(new Runnable() { @Override public void run() { threadLocal.set((int) (Math.random()*100)); threadLocal2.set("------"); System.out.println(threadLocal.get()+threadLocal2.get()+" "+Thread.currentThread().getName());; } }).start(); } threadLocal.set((int) (Math.random()*100)); threadLocal2.set("======"); System.out.println(threadLocal.get()+threadLocal2.get()+" "+Thread.currentThread().getName());; } }
ThreadLocal
线程本地存储,每个线程都拥有自己的线程本地存储,线程私有,线程隔离
This class provides thread-local variables
每个线程都拥有独立的共享变量的副本,副本只对当前线程可见,当前线程对副本的修改不会影响其他线程
ThreadLocal实例通常定义为private static
只要线程还是活的并且ThreadLocal实例是可访问的,每个线程都保持对线程本地变量的副本的隐式引用
线程死亡之后,垃圾回收回回收线程的本地变量的所有副本
Thread类的threadLocals属性在调用ThreadLocal的get或者set方法时初始化
ThreadLocal是如何为每个线程创建共享变量的副本的?
Thread类有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,threadLocals就用来存储每个线程的变量副本
创建线程、线程初始化之后,threadLocals仍为null,在调用ThreadLocal对象的get或者set方法时,才会初始化threadLocals
变量的副本实际存储在Thread类的threadLocals成员变量中
threadLocals是ThreadLocal.ThreadLocalMap类型,ThreadLocalMap是ThreadLocal的静态内部类,成员内部类Entry
存储当前线程的变量副本:private void set(ThreadLocal key, Object value)
键:this,当前ThreadLocal类的实例,因为一个线程可能有多个ThreadLocal对象,所以使用ThreadLocal类的实例作为ThreadLocalMap的键
值:value,要保存的变量的副本
在当前线程中使用变量副本:private Entry getEntry(ThreadLocal key) entry.value
1.get(调用ThreadLocal对象的get方法,获取当前线程的threadlocals成员变量中保存的键为this的value,该value值即为线程的变量副本)
(1)获取当前线程t Thread.currentThread()
(2)调用getmap(t),获取与当前线程绑定的ThreadLocalMap对象map
getMap方法:直接返回当前线程的成员变量threadLocals(ThreadLocal.ThreadLocalMap类型)
(3)如果map不为空,从map获取ThreadLocal在当前线程中保存的变量副本
map.getEntry(this) this是ThreadLocal的对象
如果从map中找到了,直接返回
(4)如果map为空,或者从map中找不到键this对应的Entry
调用setInitialValue()方法
在setInitialValue()方法中
首先,返回初始化值value,调用initialValue()(protected方法,返回null,因此,如果在set之前get,会空指针异常;一定要在get之前set,就应该在创建private static的ThreadLocal对象时,重写initialValue方法,返回一个初始化值)
然后,如果map是空,调用createMap(t,value)初始化当前线程的threadlocals成员变量
如果map不为空(当前map中,没有this对应的Entry),map.set(this,value)
2.set(value)(调用ThreadLocal对象的set方法,在当前线程的threadLocals成员变量中保存一个键为this,值为value的共享变量副本)
(1)获得当前线程t
Thread.currentThread();
(2)获得当前线程的ThreadLocalMap成员变量
ThreadLocalMap map = getMap(t);(getMap方法直接返回t.threadLocals)
(3)map不为空
map.set(this, value);
(4)map为空
createMap(t, value);(在createMap方法中,初始化Thread类的threadLocals成员变量t.threadLocals = new ThreadLocalMap(this, firstValue);)
3.remove(调用ThreadLocal对象的remove方法,移除当前线程的threadlocals对象中键为this的entry)
(1)获得当前线程的ThreadLocalMap对象
ThreadLocalMap m = getMap(Thread.currentThread());
(2)m.remove(this);
ThreadLocalMap
ThreadLocal的静态内部类
Thread类的ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,该成员变量实际就是存储了当前线程的变量副本(map)
Entry继承WeakReference
弱引用问题
ThreadLocal已经被回收了,ThreadLocalMap中可能存在键为null的键值对
解决:将ThreadLocal定义为private static
调用remove清除