ThreadLocal引入
该类主要实现每一个线程都有自己共享变量。该类提供了线程局部变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。
ThreadLocal使用
public static void main(String[] args) {
ThreadLocal tl = new ThreadLocal<>();
if(tl.get()==null) {
System.out.println("是空值哦");
tl.set("我的值");
}
System.out.println(tl.get());
System.out.println(tl.get());
}
常用的方法通过set、get方法去操作当前线程的变量。
ThreadLocal get第一次返回为null问题
重写initialValue。
static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {//这里就相当于构造一个匿名类继承了ThreadLocal,并且重写了initialValue方法。
protected Integer initialValue() {
return 999;
};
};
public static void main(String[] args) {
System.out.println(tl.get());
tl.set(666);
System.out.println(tl.get());
}
ThreadLocal存取每一个线程共享变量的隔离性
每一个线程的本地变量,之间互相不干预。
package com.example.test;
public class Test250 {
static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return (int) (Math.random()*6+9);
};
};
public static void main(String[] args) {
Thread t = new Thread(new MyThread250A(tl));
t.start();
for(int i = 0;i<10;i++) {
System.out.println("main get"+tl.get());
}
}
}
class MyThread250A implements Runnable{
private ThreadLocal<Integer> tl;
public MyThread250A(ThreadLocal<Integer> tl) {
super();
this.tl = tl;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<10;i++) {
System.out.println("A get"+tl.get());
}
}
}
ThreadLocal实现原理
public T get() {
Thread t = Thread.currentThread();//获取当前运行的线程对象
ThreadLocalMap map = getMap(t);
// ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values.ThreadLocalMap 这个类是一个定制的Hash Map 用来存放线程本地地。threadLocals是定义在Thread类中的一个公有属性,需要闯入当前运行的线程,他是ThreadLocalMap 的实例。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
//如果该运行的线程对应的ThreadLocalMap 是空也就是threadLocals为空。
}
//Entry 条目中,将ThreadLocal的实例对象作为键,存入的值作为value,保存到Map中
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private T setInitialValue() {
T value = initialValue();//调用ThreadLocal对象的默认初始化方法,取得值。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//存放键值对
else
createMap(t, value);//会构造一个ThreadLocalMap,即 t.threadLocals
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);//存放键值
}
- 首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,根据 Thread.currentThread(),拿到该线程的 threadlocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal的对象,value为变量副本(即你要存储的值)。
- 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
- 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
一个线程可以存放多个ThreadLocal对象
ThreadLocal<Long> tl = new ThreadLocal<>();
ThreadLocal<String> tl2 = new ThreadLocal<>();
tl.set(Thread.currentThread().getId());
tl2.set(Thread.currentThread().getName());
System.out.println(tl.get());
System.out.println(tl2.get());
我们上面讲述了ThreadLocal实际上是通过每一个正在运行的线程的threadLocals,也就是一个map,其中键是ThreadLocal的实例对象,所以是可以有多个不同的。
InheritableThreadLocal
首先该类是继承于ThreadLocal,该类扩展了 ThreadLocal,为子线程提供从父线程那里继承的值:在创建子线程时,子线程会接收所有可继承的线程局部变量的初始值,以获得父线程所具有的值。通常,子线程的值与父线程的值是一致的。
static InheritableThreadLocalTest tt = new InheritableThreadLocalTest();
public static void main(String[] args) {
/*MyThread260 mt = new MyThread260(tt);
Thread t = new Thread(mt);*/
//如果放在这里达不到继承父线程的值,因为此时父类的threadLocals 还没有设值哦。
for(int i=0;i<10;i++) {
System.out.println("main"+tt.get());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MyThread260 mt = new MyThread260(tt);
Thread t = new Thread(mt);
t.start();
}
}
class MyThread260 implements Runnable{
private InheritableThreadLocalTest t;
public MyThread260(InheritableThreadLocalTest t) {
super();
this.t = t;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++) {
System.out.println("子线程"+t.get());
}
}
}
在这里已定义要注意一点,就是你必须在父类线程threadLocals 设置好了,创建子线程,这样才可以继承于父线程的,才可以获取,这样值才相等。
public class InheritableThreadLocalTest extends InheritableThreadLocal{
@Override
protected Object initialValue() {
// TODO Auto-generated method stub
return new Date().getTime();
}
}
InheritableThreadLocal的原理
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
获取线程的私有值,可以通过threadLocals ,子线程要获取父线程的值,通过inheritableThreadLocals ,其实两者差不多。
那它是如何传递值的呢:
关键在于:
Thread thread = new Thread();
我们在线程构造方法中,找到如下:
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
当我们在创建一个新的线程的时候,它会首先去检查父线程inheritableThreadLocals ,如果不为空,那么子线程,就拿过来。
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;//获取该值。
}
值继承并修改
重写childValue,会在原来的基础上,将父类的值进行修改。
@Override
protected Object childValue(Object parentValue) {
// TODO Auto-generated method stub
return super.childValue(parentValue)+"我修改了";
}
InheritableThreadLocal注意点
在子线程取得父线程的值的时候,子线程就已经确定了 ThreadLocal.ThreadLocalMap inheritableThreadLocals ,而如果你这时在修改inheritableThreadLocals 的值,子线程读到的还是原来的旧值。