ThreadLocal的作用
当在多线程环境下,每个线程都可以对共享变量进行操作,如果我们希望每个线程都有专属于自己的变量,不被其他线程影响,那么我们可以使用ThreadLocal实现专属于线程的副本变量。例如常见的SimpleDateFormat
是线程不安全的,而我们又不希望每次使用都创建一个,那么我们可用使用ThreadLocal封装
private static final ThreadLocal<SimpleDateFormat> FORMAT_THREAD_LOCAL = ThreadLocal
.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
countDownLatch.await();
SimpleDateFormat simpleDateFormat = FORMAT_THREAD_LOCAL.get();
Date now = new Date();
String format = simpleDateFormat.format(now);
System.out.println(Thread.currentThread().getName() + ":before:" + format);
// 修改只对当前线程有效
FORMAT_THREAD_LOCAL.set(new SimpleDateFormat("yyyy-MM-dd"));
System.out.println(Thread.currentThread().getName() + ":after:" +FORMAT_THREAD_LOCAL.get().format(now));
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread" + i).start();
}
countDownLatch.countDown();
}
ThreadLocal原理
先看下Thread
类的属性
class Thread implements Runnable {
//类似map,key-value关联
ThreadLocal.ThreadLocalMap threadLocals = null;
}
ThreadLocal#get
方法
public T get() {
Thread t = Thread.currentThread();
// 获取线程的Map,threadLocals
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
// 从map中返回值
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 初始化map
return setInitialValue();
}
private T setInitialValue() {
// 初始化构建方法。jdk8支持withInitial创建
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//保存值,关联当前线程,key为当前线程
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
ThreadLocal
通过管理当前线程的ThreadLocalMap实现变量的线程隔离,每个线程只都保存自己的副本,ThreadLocal提供中间人的作用,供调用者获取线程的本地值。
网友评论