ThreadLocal简介
ThreadLocal
叫做线程变量,意思是ThreadLocal
中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal
为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
简单使用
@Test
public void testThreadLocal() {
ThreadLocal<String> localVar = new ThreadLocal<>();
System.out.println("main-var-before:" + localVar.get());
localVar.set("2222");
new Thread(() -> {
System.out.println("thread-var-before:" + localVar.get());
localVar.set("111");
System.out.println("thread-var-after:" + localVar.get());
}).start();
System.out.println("main-var-after:" + localVar.get());
}
输出结果:
main-var-before:null
main-var-after:2222
thread-var-before:null
thread-var-after:111
源码分析
ThreadLocal
ThreadLocal#set
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal#getMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal#createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal#get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap
构造方法,以ThreadLocal
对象为key,value存储变量。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
Entry
构造方法
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
强软弱虚
- 强引用:gc时不会回收
- 软引用:只有在内存不够用时,gc才会回收
- 弱引用:只要gc就会回收
- 虚引用:是否回收都找不到引用的对象,仅用于管理直接内存。
MDC
简单示例
@Test
public void testMDC() {
MDC.put("111", "222");
String mdc = MDC.get("111");
System.out.println(mdc);
}
源码解析
LogbackMDCAdapter#put
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
} else {
Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
Integer lastOp = this.getAndSetLastOperation(1);
if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
oldMap.put(key, val);
} else {
Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
}
}
}
LogbackMDCAdapter#get
public String get(String key) {
Map<String, String> map = (Map)this.copyOnThreadLocal.get();
return map != null && key != null ? (String)map.get(key) : null;
}
InheritableThreadLocal
简单介绍
InheritableThreadLocal
主要用于子线程创建时,需要自动继承父线程的ThreadLocal
变量
简单示例
@Test
public void testInheritableThreadLocal() {
ThreadLocal<String> localVar = new InheritableThreadLocal<>();
System.out.println("main-var-before:" + localVar.get());
localVar.set("2222");
new Thread(() -> {
System.out.println("thread-var-before:" + localVar.get());
localVar.set("111");
System.out.println("thread-var-after:" + localVar.get());
}).start();
System.out.println("main-var-after:" + localVar.get());
}
输出结果:
main-var-before:null
main-var-after:2222
thread-var-before:2222
thread-var-after:111
原理分析
InheritableThreadLocal
存储和获取数据,是通过线程中的属性inheritableThreadLocals
。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
Thread#init()
,在线程创建的时候调用。子线程会复制父线程的inheritableThreadLocals
的数据。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
TransmittableThreadLocal
应用背景
父子线程传递本地变量可以通过InheritableThreadlocoal
进行传递,但是如果采用线程池,不一定能传递,因为在线程在线程池中的存在不是每次使用都会进行创建,InheritableThreadlocal
是在线程初始化时intertableThreadLocals=true
才会进行拷贝传递。所以若本次使用的子线程是已经被池化的线程,从线程池中取出线下进行使用,是没有经过初始化的过程,也就不会进行父子线程的本地变量拷贝。
简单使用
- 首次调用的时候,通过
InheritableThreadLocal
获取到父线程的数据 - 第二次调用的时候,父线程的数据发生变更,线程池创建的线程是获取不到最新的父线程的数据的。
@Test
public void testTransmittableThreadLocal() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService = TtlExecutors.getTtlExecutorService(executorService);
// InheritableThreadLocal<String> username = new InheritableThreadLocal<>();
TransmittableThreadLocal<String> username = new TransmittableThreadLocal<>();
// ①
username.set("zhangShang");
executorService.submit(new Runnable() {
@Override
public void run() {
log.info(username.get());
}
});
// ②
username.set("liSi");
executorService.submit(new Runnable() {
@Override
public void run() {
log.info(username.get());
}
});
}
源码解析
TtlExecutors#getTtlExecutorService
,创建线程池
public static ExecutorService getTtlExecutorService(ExecutorService executorService) {
return (ExecutorService)(executorService != null && !(executorService instanceof ExecutorServiceTtlWrapper) ? new ExecutorServiceTtlWrapper(executorService) : executorService);
}
ExecutorServiceTtlWrapper#submit(java.lang.Runnable)
,提交任务
public Future<?> submit(Runnable task) {
return this.executorService.submit(TtlRunnable.get(task));
}
TtlRunnable#get(java.lang.Runnable)
,封装任务。
public static TtlRunnable get(Runnable runnable) {
return get(runnable, false, false);
}
TtlRunnable#run
,执行任务。执行任务会将TransmittableThreadLocal
的数据备份。备份数据,等到子线程运行结束,恢复之前的数据。
public void run() {
Map<TransmittableThreadLocal<?>, Object> copied = (Map)this.copiedRef.get();
if (copied != null && (!this.releaseTtlValueReferenceAfterRun || this.copiedRef.compareAndSet(copied, (Object)null))) {
Map backup = TransmittableThreadLocal.backupAndSetToCopied(copied);
try {
this.runnable.run();
} finally {
TransmittableThreadLocal.restoreBackup(backup);
}
} else {
throw new IllegalStateException("TTL value reference is released after run!");
}
}