多线程访问同一个变量的时候,很容易出现问题,特别是多线程对一个共享变量进行写入的时候。为了线程的安全在进行数据写入时候会进行数据的同步。
如上图: 如果要想实现多个线程操作同一个数据而不被其他线程所影响,就必须加锁操作,加锁又增加了使用的复杂度。
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是访问不到的**,主线程**也访问不到。
由下图可知 Thread类中有一个成员变量 ThreadLocal.ThreadLocalMap **threadLocals **= **null ** 是ThreadLocalMap类型的,本地变量都是存储在ThreadLocalMap中的,ThreadLocal只是一个外壳。ThreadLocalMap是ThreadLocal中的内部类,是一个定制化的Map形式,那就代表一个线程可以有多个ThreadLocal本地变量。key就是ThreadLocal本身,value就是存储的本地变量。
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);
}
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;
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
从上面的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应用而生。
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);
}
}
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);
}
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();
}
}