多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,
为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施。
同步一般都是加锁,这样使用成本比较高,需要对锁有一定的了解。
于是jdk提供了另外一种措施,如果你创建了线程本地变量,那么访问这个变量的每个线程都有这个变量的一个本地副本,
多线程操作这个变量时,实际操作的是自己的本地内存里的变量,很好的解决了线程安全问题。
使用示例
package com.pimee.thread;
public class ThreadLocalTest {
static ThreadLocal<String> localParam = new ThreadLocal<>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localParam.get());
//清除本地内存中的本地变量
// localVar.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localParam.set("localParam1");
//调用打印方法
print("thread1");
//打印本地变量
System.out.println("after remove : " + localParam.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localParam.set("localParam2");
//调用打印方法
print("thread2");
//打印本地变量
System.out.println("after remove : " + localParam.get());
}
});
t1.start();
t2.start();
}
}
探究实现原理
Thread类中有两个变量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部类ThreadLocalMap类型的变量,我们通过查看内部内ThreadLocalMap可以发现实际上它类似于一个HashMap。
- 分析set、get、remove方法实现逻辑
- 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);
}
// 根据线程获取ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上
}
// 创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 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() {
//
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;
}
protected T initialValue() {
return null;
}
- remove方法
public void remove() {
//获取当前线程绑定的threadLocals
ThreadLocalMap m = getMap(Thread.currentThread());
//如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
if (m != null)
m.remove(this);
}
ThreadLocal不支持继承性
看个例子
public class TestThreadLocal {
//(1)创建ThreadLocal变量
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
//在main线程中添加main线程的本地变量
threadLocal.set("mainVal");
//新创建一个子线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程中的本地变量值:"+threadLocal.get());
}
});
thread.start();
//输出main线程中的本地变量值
System.out.println("main线程中的本地变量值:"+threadLocal.get());
}
}
输出结果:
main线程中的本地变量值: mainVal
子线程中的本地变量值:null