記事のディレクトリ
前回の記事では、非同期スレッド間でクロススレッドThreadLocal転送を実現する必要があるリンク追跡シナリオについて説明しました。InheritableThreadLocalは単純なシナリオで使用できますが、ITLはサブスレッドで初期化されるため、スレッドプールシナリオには適用できません。親スレッドのThreadLocalがコピーされますが、プーリングシナリオでは、子スレッドは複数回再利用されますが、ITLは子スレッドが初めて作成されたときにのみThreadLocalを渡すことができ、その後の再利用ではThreadLocalをリセットできません。 。。そのため、TransmittableThreadLocalが登場しました。これにより、スレッドプーリングシナリオでのThreadLocalの送信の問題を解決できます。
指示
-
依存関係を導入する
<dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.11.4</version> </dependency>
-
TransmittableThreadLocalオブジェクトを作成します
-
Runnableオブジェクトをスレッドプールに送信するときは、TtlRunnableを使用してラップします
このようにして、親スレッドと子スレッドの間のThreadLocal変数の転送を完了でき、InheritableThreadLocalは表示されません。
スレッドプールシナリオの問題
import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author yogurtzzz
* @date 2020/5/28 15:14
**/
public class ThreadLocalTest {
private static ThreadLocal<String> ttl = new TransmittableThreadLocal<>();
/** 保证只有1个线程,以便观察这个线程被多个Runnable复用时,能否成功完成ThreadLocal的传递 **/
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,1,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10)
);
public static void main(String[] args) throws InterruptedException {
ttl.set("yogurtzzz");
for (int i = 0; i < 5; i++) {
if (i == 2) {
ttl.set("changed");
}
TtlRunnable runnable = TtlRunnable.get(() -> {
System.out.println(Thread.currentThread().getName() + " : " + ttl.get());
});
threadPoolExecutor.execute(runnable);
//CompletableFuture.runAsync(runnable,threadPoolExecutor);
TimeUnit.MILLISECONDS.sleep(500);
}
}
}
結果:
原理
- TTLはInheritableThreadLocalから継承します
- ホルダーを介して、各スレッドによって現在保持されているすべてのThreadLocalオブジェクトが保存されます
- TtlRunnableのgetメソッドを使用して、Runnableオブジェクトをラップします。オブジェクトをラップするとき、SNAPSHOTおよびスナップショットと同様のメカニズムを使用して、ホルダーを介して親スレッドによって現在保持されているすべてのThreadLocalをキャプチャします。その後、子スレッドが開始されます。Runnableオブジェクトがrunメソッドを実行する前に、親スレッドによって保持されている以前にキャプチャされたThreadLocalオブジェクトがホルダーから取得され、現在の子スレッドに設定されます。子スレッドの元のThreadLocalが保存されます。設定前。backUp、子スレッドの実行が終了したら、backUpを介して元のThreadLocalを復元します。
手書きのソースコード
以下はTTLのコアアイデアを模倣し、実装の簡単なバージョンを手作業で記述します。これにより、スレッド間でのThreadLocalの受け渡しの問題も解決できます。
- CoreThreadLocal
import java.util.Iterator;
import java.util.WeakHashMap;
/**
* @author yogurtzzz
* @date 2020/5/28 15:11
*
* 简单版TransmittableThreadLocal
* 用于处理InheritableThreadLocal在线程池化场景下的问题
* 需配合CoreRunnable使用
**/
public class CoreThreadLocal<T> extends InheritableThreadLocal<T> {
/**
* 用来保存当前线程持有的threadLocal
* 它是static的
* 所有线程都会有一个holder的threadLocal
* 这个holder是一个map,保存着当前线程所持有的threadLocal
* **/
private static InheritableThreadLocal<WeakHashMap<CoreThreadLocal<Object>,?>> holder =
new InheritableThreadLocal<WeakHashMap<CoreThreadLocal<Object>,?>>() {
@Override
protected WeakHashMap<CoreThreadLocal<Object>, ?> childValue(WeakHashMap<CoreThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap<>(parentValue);
}
@Override
protected WeakHashMap<CoreThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<>();
}
};
@Override
public T get() {
T value = super.get();
if (null != value) {
addToHolder();
}
return value;
}
@Override
public void set(T value) {
if (null == value) {
removeFromHolder();
super.remove();
}else {
super.set(value);
addToHolder();
}
}
private void addToHolder() {
if (!holder.get().containsKey(this)) {
holder.get().put((CoreThreadLocal<Object>)this,null);
}
}
private void removeFromHolder() {
holder.get().remove(this);
}
static class Transmitter {
/** 捕捉当前父线程的threadLocal **/
public static SnapShot capture() {
return new SnapShot(captureCtlValues());
}
private static WeakHashMap<CoreThreadLocal<Object>, Object> captureCtlValues() {
WeakHashMap<CoreThreadLocal<Object>,Object> ctlValues = new WeakHashMap<>();
/** 从holder中取当前线程持有的threadLocal **/
for (CoreThreadLocal<Object> ctlItem : holder.get().keySet()) {
ctlValues.put(ctlItem,ctlItem.get());
}
return ctlValues;
}
/**
* 将capture设置到当前线程,并保存当前线程原有的threadLocal,返回
* **/
public static CoreThreadLocal.SnapShot replay(CoreThreadLocal.SnapShot snapShot) {
WeakHashMap<CoreThreadLocal<Object>, Object> capture = snapShot.ctlValue;
WeakHashMap<CoreThreadLocal<Object>, Object> backValue = new WeakHashMap<>();
/**
* 从holder中获取当前线程持有的threadLocal的Map,进行迭代保存
* **/
Iterator<CoreThreadLocal<Object>> iterator = holder.get().keySet().iterator();
while (iterator.hasNext()) {
CoreThreadLocal<Object> threadLocal = iterator.next();
backValue.put(threadLocal,threadLocal.get());
if (!capture.containsKey(threadLocal)) {
iterator.remove();
threadLocal.remove();
}
}
/**
* 设置上capture
* */
setThreadLocal(capture);
return new SnapShot(backValue);
}
private static void setThreadLocal(WeakHashMap<CoreThreadLocal<Object>, Object> ctlValues) {
ctlValues.forEach((key, value) -> {
key.set(value);
});
}
/**
* 恢复为backUp
* **/
public static void restore(CoreThreadLocal.SnapShot backUp) {
Iterator<CoreThreadLocal<Object>> iterator = holder.get().keySet().iterator();
while (iterator.hasNext()) {
CoreThreadLocal<Object> threadLocal = iterator.next();
if (!backUp.ctlValue.containsKey(threadLocal)) {
iterator.remove();
threadLocal.remove();
}
}
setThreadLocal(backUp.ctlValue);
}
}
static class SnapShot {
final WeakHashMap<CoreThreadLocal<Object>,Object> ctlValue;
private SnapShot(WeakHashMap<CoreThreadLocal<Object>,Object> ctlValue) {
this.ctlValue = ctlValue;
}
}
}
- CoreRunnable
public class CoreRunnable implements Runnable{
private AtomicReference<CoreThreadLocal.SnapShot> captureRef;
private Runnable runnable;
private CoreRunnable(Runnable runnable) {
this.runnable = runnable;
captureRef = new AtomicReference<>(Transmitter.capture());
}
@Override
public void run() {
CoreThreadLocal.SnapShot capture = captureRef.get();
CoreThreadLocal.SnapShot backUp = Transmitter.replay(capture);
try{
runnable.run();
}finally {
Transmitter.restore(backUp);
}
}
/** 调用这个函数进行包装 **/
public static CoreRunnable getRunnable(Runnable runnable) {
return new CoreRunnable(runnable);
}
}
- テスト
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author yogurtzzz
* @date 2020/6/4 13:05
**/
public class Test {
private static ThreadLocal<String> ttl = new CoreThreadLocal<>();
/** 保证只有1个线程,以便观察这个线程被多个Runnable复用时,能否成功完成ThreadLocal的传递 **/
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,1,0,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10)
);
public static void main(String[] args) throws InterruptedException {
ttl.set("yogurtzzz");
for (int i = 0; i < 5; i++) {
if (i == 2) {
ttl.set("changed");
}
CoreRunnable runnable = CoreRunnable.getRunnable(() -> {
System.out.println(Thread.currentThread().getName() + " : " + ttl.get());
});
threadPoolExecutor.execute(runnable);
//CompletableFuture.runAsync(runnable,threadPoolExecutor);
TimeUnit.MILLISECONDS.sleep(500);
}
}
}
結果: