1 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);
}
ThreadLocalMap保存在Thread中:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可见threadLocals为Thread的内部变量,每个Thread类的实例,都有自己的threadLocals变量。
// Thread类源码
public class Thread implements Runnable {
......
ThreadLocal.ThreadLocalMap threadLocals = null;
......
}
1.1 总结
ThreadLocal属性保存在每个线程实例中,线程实例通过Map结构管理所有的ThreadLocal属性,Map的key为ThreadLocal属性。
1.2 遇到的问题
属性在父子线程中传递的问题:
public class MainToChild implements Runnable {
public static ThreadLocal<String> threadLocal = new ThreadLocal();
@Override
public void run() {
System.out.println("child : " + Thread.currentThread().getName());
System.out.println("child : " + threadLocal.get());
}
public static void main(String[] args) {
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
threadLocal.set("main");
System.out.println("main : " + Thread.currentThread().getName());
System.out.println("main : " + threadLocal.get());
}
}
ThreadLocal属性是Thread的内部变量,父子线程是不同的线程实例,显然在子线程中无法获取父线程设置的属性:
main : main
child : Thread-0
child : null
main : main
2 InheritableThreadLocal实现原理
public class MainToChild implements Runnable {
public static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal();
@Override
public void run() {
System.out.println("child name : " + Thread.currentThread().getName());
System.out.println("child get main value : " + threadLocal.get());
threadLocal.set("child");
System.out.println("child value : " + threadLocal.get());
}
public static void main(String[] args) {
threadLocal.set("main");
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
System.out.println("main name : " + Thread.currentThread().getName());
System.out.println("main value: " + threadLocal.get());
}
}
执行结果:
main name : main
child name : Thread-0
child get main value : main
main value: main
child value : child
再看一个:
package com.sankuai.mall.consumerapi.web.controller;
import lombok.SneakyThrows;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 3:11 下午
* ---------------------------------------
* Desc :
*/
public class MainToChild implements Runnable {
public static InheritableThreadLocal threadLocal = new InheritableThreadLocal();
@SneakyThrows
@Override
public void run() {
System.out.println("child name : " + Thread.currentThread().getName());
System.out.println("1 child get main value : " + threadLocal.get());
System.out.println("2 child get main value : " + threadLocal.get());
}
public static void main(String[] args) {
threadLocal.set("main");
MainToChild mainToChild = new MainToChild();
Thread thread = new Thread(mainToChild);
thread.start();
threadLocal.set("again main");
System.out.println("main name : " + Thread.currentThread().getName());
System.out.println("main value: " + threadLocal.get());
}
}
执行结果:
child name : Thread-0
main name : main
1 child get main value : main
main value: again main
2 child get main value : main
我们先来看下类的介绍:
This class extends ThreadLocal to provide inheritance of values from parent
thread to child thread: when a child thread is created, the child receives
initial values for all inheritable thread-local variables for which the parent
has values. Normally the child's values will be identical to the parent's;
however, the child's value can be made an arbitrary function of the parent's
by overriding the childValue method in this class.
Inheritable thread-local variables are used in preference to ordinary
thread-local variables when the per-thread-attribute being maintained in
the variable (e.g., User ID, Transaction ID) must be automatically transmitted
to any child threads that are created.
再看下实现原理:
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
与ThreadLocal一样,InheritableThreadLocal保存在Thread中:
// Thread类源码
public class Thread implements Runnable {
......
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
......
}
数据结构也是一样的,我们来看这这个属性有什么特殊逻辑(when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values),下面代码在Thread类中:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
.....
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
下面的代码在ThreadLocal中:
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 需要注意childValue方法,通过重写这个方法,
// 可以实现自定义的拷贝逻辑
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
2.1 总结
如果父线程使用了InheritableThreadLocal属性,在子线程初始化的时候,会把父线程InheritableThreadLocal属性中的值,逐个拷贝到子线程的InheritableThreadLocal属性中
2.2 遇到的问题
package com.sankuai.mall.consumerapi.web.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 4:11 下午
* ---------------------------------------
* Desc :
*/
public class ITL {
public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
System.out.println(param.get());
});
param.set("main2");
pool.submit(() -> {
System.out.println(param.get());
});
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}
执行的结果:
main
main
main
InheritableThreadLocal只会在线程池中的线程初次创建的时候,从父线程拷贝属性,而父线程再次修改这个属性时,线程池中的这个线程是无法再次感知到的。
我们在日常的开发中,很少主动new线程,都是使用线程池,而线程池中,不会频繁的创建线程,更多的场景是线程创建一次,重复使用。
再看一个例子:
package com.sankuai.mall.consumerapi.web.controller;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 4:11 下午
* ---------------------------------------
* Desc :
*/
public class ITL {
public static InheritableThreadLocal<String> param = new InheritableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
System.out.println(param.get());
});
pool.submit(() -> {
param.set("child value only use in this");
System.out.println(param.get());
});
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}
执行结果:
main
child value only use in this
child value only use in this
如果线程中的属性在上次使用时被修改,下次使用后,线程中保存的属性值是上次使用时修改的值。
3 TransmittableThreadLocal实现原理
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 4:11 下午
* ---------------------------------------
* Desc :
*/
public class ITL {
public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));
pool.submit(() -> {
System.out.println(param.get());
});
param.set("main2");
pool.submit(() -> {
System.out.println(param.get());
});
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}
执行结果:
main
main2
main2
可以看到,使用TransmittableThreadLocal并配合TtlExecutors.getTtlExecutorService()可以让子线程感知到父线程对共享变量的实时变化。
再看一个:
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 4:11 下午
* ---------------------------------------
* Desc :
*/
public class ITL {
public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));
pool.submit(() -> {
System.out.println(param.get());
});
param.set("main2");
pool.submit(() -> {
param.set("child value only use in this");
System.out.println(param.get());
});
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}
执行结果:
main
child value only use in this
main2
而且子线程对共享变量的修改不会影响到其他子线程。
我们看下具体的实现原理:
public final void set(T value) {
// InheritableThreadLocal.set(),这里保留了父类的特性
super.set(value);
if (null == value) {
this.removeValue();
} else {
this.addValue();
}
}
private void addValue() {
if (!((Map)holder.get()).containsKey(this)) {
((Map)holder.get()).put(this, (Object)null);
}
}
与ThreadLocal保存在Thread中不同,holder就保存在TransmittableThreadLocal中:
private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder = new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
return new WeakHashMap();
}
protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
return new WeakHashMap(parentValue);
}
};
继续往下看就没有其它信息了,我们回过头看TtlExecutors,对普通的ExecutorService进行了封装:
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
return (ExecutorService)(!TtlAgent.isTtlAgentLoaded() && executorService != null && !(executorService instanceof TtlEnhanced) ? new ExecutorServiceTtlWrapper(executorService) : executorService);
}
我们看下ExecutorServiceTtlWrapper的实现:
public <T> Future<T> submit(@Nonnull Runnable task, T result) {
return this.executorService.submit(TtlRunnable.get(task), result);
}
public static TtlRunnable get(@Nullable Runnable runnable) {
return get(runnable, false, false);
}
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
if (null == runnable) {
return null;
} else if (runnable instanceof TtlEnhanced) {
if (idempotent) {
return (TtlRunnable)runnable;
} else {
throw new IllegalStateException("Already TtlRunnable!");
}
} else {
return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}
}
看起来核心就是TtlRunnable了:
public void run() {
Object captured = this.capturedRef.get();
if (captured != null && (!this.releaseTtlValueReferenceAfterRun || this.capturedRef.compareAndSet(captured, (Object)null))) {
//线程执行前,备份现场
Object backup = Transmitter.replay(captured);
try {
this.runnable.run();
} finally {
// 线程执行完成后,再用拷贝的属性还原现场
// 这样就可以解决InheritableThreadLocal所面临的问题
Transmitter.restore(backup);
}
} else {
throw new IllegalStateException("TTL value reference is released after run!");
}
}
我们找备份现场的代码看一下,其实就是把属性复制了一下:
public static Object replay(@Nonnull Object captured) {
Map<TransmittableThreadLocal<?>, Object> capturedMap = (Map)captured;
Map<TransmittableThreadLocal<?>, Object> backup = new HashMap();
Iterator iterator = ((Map)TransmittableThreadLocal.holder.get()).entrySet().iterator();
// 遍历需要复制的holder中的属性
while(iterator.hasNext()) {
Entry<TransmittableThreadLocal<?>, ?> next = (Entry)iterator.next();
TransmittableThreadLocal<?> threadLocal = (TransmittableThreadLocal)next.getKey();
// backup
backup.put(threadLocal, threadLocal.get());
if (!capturedMap.containsKey(threadLocal)) {
terator.remove();
threadLocal.superRemove();
}
}
// clear the TTL values that is not in captured
// avoid the extra TTL values after replay when run task
setTtlValuesTo(capturedMap);
TransmittableThreadLocal.doExecuteCallback(true);
return backup;
}
看下下面代码的执行结果:
import com.alibaba.ttl.TransmittableThreadLocal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author : mazhen
* Date : 2021/11/12
* Time : 6:28 下午
* ---------------------------------------
* Desc :
*/
public class TTL {
public static TransmittableThreadLocal<String> param =
new TransmittableThreadLocal<>();
public static void main(String[] args) {
param.set("main");
ExecutorService pool = Executors.newFixedThreadPool(1);
pool.submit(() -> {
System.out.println(param.get());
});
pool.submit(() -> {
param.set("special");
System.out.println(param.get());
});
param.set("main2");
pool.submit(() -> {
System.out.println(param.get());
});
pool.shutdown();
}
}