TL, ITL, TTL原理深度剖析

579 阅读12分钟

1. TL功能及其实现原理

1.1 概述

ThreadLocal本意上就是线程私有的数据,每个线程维护着自己的一份副本,线程与线程之间数据隔离。

1.2 实现原理

ThreadLocal的本质其实就是存储在Thread中的成员变量threadLocals中, 我们结合源码来分析, 发现其实就是一个Map

public class Thread implements Runnable {
    
    // 省略其他代码......
    
    /* ThreadLocal values pertaining to this thread. This map is maintained
    * by the ThreadLocal class.  
    */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
}

static class ThreadLocalMap {

        /**
         * 注意: 这里是一个软引用
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    
}
// set值
public void set(T value) {
	Thread t = Thread.currentThread();
	// 获取当前线程的Map
	ThreadLocalMap map = getMap(t);
	if (map != null)
		// 如果上一步获取的不为空, 则将当前TL和值存储到Map中
		map.set(this, value);
	else
		// 否则, 就创建一个新的Map
		createMap(t, value);
}

// 创建Map
void createMap(Thread t, T firstValue) {
	t.threadLocals = new ThreadLocalMap(this, firstValue);
}

// get Map中的值
public T get() {
	Thread t = Thread.currentThread();
	// 获取当前线程的Map
	ThreadLocalMap map = getMap(t);
	if (map != null) {
		ThreadLocalMap.Entry e = map.getEntry(this);
		if (e != null) {
			// 通过 ThreadLocal 在当前 Map 中获取值, 如果有的情况, 
			@SuppressWarnings("unchecked")
			T result = (T)e.value;
			return result;
		}
	}
	// 否则, 初始化一个空值
	return setInitialValue();
}

// remove 当前Map中的ThreadLocal 
public void remove() {
	 ThreadLocalMap m = getMap(Thread.currentThread());
	 if (m != null)
		 m.remove(this);
 }

1.3 局限性

问题1: 父线程无法通过ThreadLocal向子线程传递线程私有数据


问题2: 父线程无法通过ThreadLocal向线程池中线程传递线程私有数据



2. ITL功能及其实现原理

2.1 概述

InheritableThreadLocal 是 ThreadLocal 的为了解决上述问题1的解决方案

2.2 实现原理

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

	// 在第一次set() 的时候, 创建ThreadLocalMap, 将会调用子类的创建方法进行创建
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}


// 线程初始化
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
	
	// 省略其他代码......
	
	// 子线程在初始化的时候, 会给当前子线程中的inheritableThreadLocals进行赋值
	if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
	}
}

// 将父线程中的table数据, 挨个复制到子线程中, 至此就实现了父线程向子线程数据传递
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) {
				// 调用 InheritableThreadLocal 中重载方法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++;
			}
		}
	}
}

这意味着是:浅拷贝,即默认情况下,父线程传递给子线程的线程私有数据是一份浅拷贝,若拷贝指向个不可变对象,这不是什么问题;若指向一个引用,那尤其需要注意,因为不再有线程封闭,父子线程中任意的变更均会影响到对方。我们说它是可重载方法,意味着若要实现"Deep Copy",需要自行继承InheritableThreadLocal类并重写childValue方法

2.3 总结

小结一下解决方案背后的原理:使用InheritableThreadLocal,会将线程私有数据存储在inheritableThreadLocals指向的ThreadLocalMap中;在构造子线程时,将当前线程inheritableThreadLocals里的数据(ThreadLocalMap)"拷贝"给子线程的ThreadLocalMap,子线程因此可以通过tl.get()取到数据,如此便实现了父线程向子线程传递线程私有数据

2.4 局限性

问题1: 父线程无法通过ThreadLocal向线程池中线程传递线程私有数据

3. TTL功能及其实现原理

3.1 概述

在日常开发过程中,由于构造与销毁子线程开销大,因此每次在业务代码中重新构造一个子线程的方式并不常见,更常见的方式是将线程池化(线程池),由线程池的调度策略决定线程们如何执行提交给池中的任务,避免了重复构造与销毁线程的开销。上面我们提到,InheritableThreadLocal可以解决父线程向子线程传递线程私有数据的问题,但一旦子线程池化之后,InheritableThreadLocal也将不再起作用, 在局限性一解决方案原理中已经阐述:子线程的ThreadLocalMap数据是在创建线程的那一刻从父线程中"拷贝"而来,此后再也没有促使其变化的地方,而子线程由于池化复用的缘故,ThreadLocalMap一直持有的是线程创建时刻的数据,此后无论进行多少次方法调用,在(池化)子线程中通过tl.get()取出来的永远是第一次传递的数据

3.2 实现原理

仔细推敲琢磨一下,我们需要的并不是创建线程的那一刻父线程的ThreadLocal值,而是提交任务时父线程的ThreadLocal值。或者换种表述方式,需要把任务提交给线程池时的ThreadLocal值传递到任务执行时
具体思路是:父线程把任务提交给线程池时一同附上此刻自己的ThreadLocalMap,包装在task里,待线程池中某个线程执行到该任务时,用task里的ThreadLocalMap赋盖当前线程ThreadLocalMap,这样就完成了父线程向池化的子线程传递线程私有数据的目标。为了避免数据污染,待任务执行完后,线程归还回线程池之前,还需要还原ThreadLocalMap,如下示:

上面的步骤一共有6步,其中2、4、6是线程池本身的提供的能力,不需要改动,只有1、3、5有所不同,我们逐一剖析

第1步疑问:提交Task的时候如何将ThreadLocalMap一同提交上去?此处难点在于如何获取当前线程的ThreadLocalMap

ThreadLocalMap是线程私有变量,只会被ThreadLocal维护,对于外部类而言是不可见的,因此要操作ThreadLocalMap就得通过ThreadLocal(操作ThreadLocal本质上是在操作当前线程的ThreadLocalMap)

首先自定义Task,用于包装维护父线程ThreadLocalMap

public class MyTask implements Runnable {
        // key是ThreadLocal,value是对应父线程的线程私有数据
    private final Map<ThreadLocal<Object>, Object> threadLocalValues;

    public MyTask(ThreadLocal<Object>... threadLocals) {
        this.threadLocalValues = new HashMap<>(threadLocals.length);
        capture(threadLocals);
    }

    private void capture(ThreadLocal<Object>[] threadLocals) {
        for (ThreadLocal<Object> threadLocal : threadLocals) {
            threadLocalValues.put(threadLocal, threadLocal.get());
        }
    }

    @Override
    public void run() {
        // todo
    }
}

使用时:

ThreadLocal<Object> tl1 = new ThreadLocal<>();
tl1.set("111111");

ThreadLocal<Object> tl2 = new ThreadLocal<>();
tl2.set("222222");


ExecutorService executorService = Executors.newFixedThreadPool(1);
// 将父线程的ThreadLocal传进去,就能取到相应的值
executorService.execute(new MyTask(tl1, tl2));

tl1.remove();
tl2.remove();

如此这般,提交到线程池的MyTask就包含了父线程的ThreadLoalMap数据。我们把"拷贝"父线程TheadLocalMap的行为称为capture(拍照),一个很生动形象的词:将父线程提交任务时刻的ThreadLocal值拍个快照并保存起来,后续使用

第3步疑问:如何用父线程的ThreadLocalMap覆盖当前执行任务线程的ThreadLocalMap?

我们可以想像到,代码正执行到MyTask#run()方法,在该方法内部,能感知的上下文环境是正执行该方法的线程,以及MyTask维护的threadLocalValues(快照),除此之外,它获取不了任何外界信息–> 这称之为线程封闭

因此可以比较自然地推理出,是要用MyTask的threadLocalValues(快照)去覆盖当前线程的ThreadLocalMap。我们称这个动作为replay(重放)

public class MyTask implements Runnable {
   // ...(省略)

    @Override
    public void run() {
        replay();
        // do biz
    }

    // 重放,用快照去覆盖当前线程的ThreadLocalMap
    private void replay() {
        for (Map.Entry<ThreadLocal<Object>, Object> entry : threadLocalValues.entrySet()) {
            ThreadLocal<Object> threadLocal = entry.getKey();
            threadLocal.set(entry.getValue());
        }
    }
}

第5步疑问:如何把当前线程的ThreadLocalMap还原?

Task业务逻辑执行完之后,毫无疑问需要将ThreadLocalMap还原,否则可能产生数据污染的风险

能够还原的前提是,用快照去覆盖当前线程的ThreadLocalMap之前,先将当前的ThreadLocal值保存起来,因此,修改代码如下:

private Object replay() {
        // 保存当前的ThreadLocal值
    Map<ThreadLocal<Object>, Object> backup = new HashMap<>();
    for (ThreadLocal<Object> threadLocal : threadLocalValues.keySet()) {
        backup.put(threadLocal, threadLocal.get());
    }

    for (Map.Entry<ThreadLocal<Object>, Object> entry : threadLocalValues.entrySet()) {
        ThreadLocal<Object> threadLocal = entry.getKey();
        threadLocal.set(entry.getValue());
    }

    return backup;
}

业务代码执行完之后,进行restore(还原):

public class MyTask implements Runnable {
    // ...(省略)
    
    @Override
    public void run() {
        Object backup = replay();
        try {
            // do biz
        } finally {
            restore(backup);
        }
    }
        
        // 还原
    private void restore(Object obj) {
        Map<ThreadLocal<Object>, Object> backup = (Map<ThreadLocal<Object>, Object>) obj;

        for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
            ThreadLocal<Object> threadLocal = entry.getKey();
            threadLocal.set(entry.getValue());
        }
    }
}

经过上面的1、3、5步分析,我们已经把所有关键问题都分析完毕,因此,做一个小实验看看结果,测试代码:

ExecutorService executorService = Executors.newFixedThreadPool(1);
ThreadLocal<Object> tl1 = new ThreadLocal<>();
ThreadLocal<Object> tl2 = new ThreadLocal<>();


// ------------第一次调用 start -------------------
tl1.set("1111");
tl2.set("2222");

executorService.execute(new MyTask(tl1, tl2));

tl1.remove();
tl2.remove();
// ------------第一次调用 end -------------------


// ------------第二次调用 start -------------------
tl1.set("3333");
tl2.set("4444");

executorService.execute(new MyTask(tl1, tl2));

tl1.remove();
tl2.remove();
// ------------第二次调用 end -------------------
public void run() {
    Object backup = replay();
    try {
        // do biz
        doBiz();
    } finally {
        restore(backup);
    }
}

private void doBiz() {
        // 打印父线程提交任务时的ThreadLocal值
    Set<ThreadLocal<Object>> threadLocals = threadLocalValues.keySet();
    for (ThreadLocal<Object> threadLocal : threadLocals) {
        System.out.println(threadLocal.get());
    }
}

证明是可行的 但是这样还不够,稍稍有些经验的朋友应该能感受到,上面的写法仅是达到了"可用"的程度,离"好用"还有一段距离:业务在向线程池提交任务的时候,需要每次都构建自定义的Task,并将ThreadLocal的引用传入,而且Task糅进了TheadLocal管理的逻辑,这样其实形成了"业务侵入性",没有做到与业务解耦,这样的代码是不可维护的

TheadLocal管理的逻辑,业务代码不应该关心,因此为了与业务解耦,容易想到的一种解决方案是:代理。通过代理可以实现对被代理类的逻辑增强,并将通用的非业务逻辑与业务代码隔离开来(设计模式萦绕心头)

提及代理,熟悉的同学对于套路了然于心:定义代理类,实现与被代理类相同的接口,并在内部维护被代理类的实例,之后就可以对被代理的方法实现额外逻辑,来增强被代理类,代码变更后如下:

public class MyTask implements Runnable {
    private Runnable task;
    private final Map<ThreadLocal<Object>, Object> threadLocalValues;

    public MyTask(Runnable task, ThreadLocal<Object>... threadLocals) {
        this.task = task;
        this.threadLocalValues = new HashMap<>(threadLocals.length);
        capture(threadLocals);
    }

    // ...(省略)

    @Override
    public void run() {
        Object backup = replay(); // 增强的逻辑
        try {
            task.run(); // 执行业务代码
        } finally {
            restore(backup); // 增强的逻辑
        }
    }
}

使用方式

executorService.execute(new MyTask(() -> {
    // do biz
}, tl1, tl2));

这样提交任务时,业务开发只需要关心()->{// do biz} 业务逻辑,而不需要关心MyTask代理类如何实现的增强逻辑,做到了与业务代码的解耦

但是,上面的代码书写起来仍然有些别扭:需要业务方主动传递tl1、tl2,说明业务方仍然需要有一定程度的参与,那能不能更彻底一些,连传递tl1、tl2的行为都省去呢?答案是肯定的

如果要避免父线程"主动"传递ThreadLocal的行为,那么就必须要知道父线程往ThreadLocalMap放数据这件事,并且在事件发生的时候将ThreadLocal引用保存下来;同时,如果父线程调用ThreadLocal#remove方法清除数据,也需要将保存下来的ThreadLocal引用一同清除掉

因此,需要自定义ThreadLocal类:

public class MyThreadLocal<T> extends ThreadLocal<T> {

    private static MyThreadLocal<HashSet<MyThreadLocal<Object>>> holder =
            new MyThreadLocal<HashSet<MyThreadLocal<Object>>>() {
                @Override
                protected HashSet<MyThreadLocal<Object>> initialValue() {
                    return new HashSet<>();
                }
            };

    // 重写set方法
    @Override
    public void set(T value) {
        super.set(value);
        addThisToHolder();
    }
        
        // 将ThreadLocal引用记录下来
    private void addThisToHolder() {
        if (!holder.get().contains(this)) {
            holder.get().add((MyThreadLocal<Object>) this);
        }
    }
    
    // 重写remove方法
    @Override
    public void remove() {
        super.remove();
        removeThisFromHolder();
    }
    
        // 将ThreadLocal引用记录移除
    private void removeThisFromHolder() {
        holder.get().remove(this);
    }
}

重写set方法,在ThreadLocal#set方法执行时将ThreadLocal引用记录下来,保存在类成员变量holder中;重写remove方法,在ThreadLocal#remove方法执行时将ThreadLocal引用一并移除
接下来,父线程向线程池提交Task,不再传递ThreadLocal的引用,那又怎么完成capture的动作呢?(如果看官们忘记了,请往上翻,前文在执行capture方法时,入参是ThreadLocal引用数组)

既然我们将ThreadLocal的引用保存在MyThreadLocal#holder这个静态变量中,那我们想办法暴露holder,不就可以得到capture需要的入参了吗?

这种思路诚然是可行的,但从面向对象设计角度而言却不是最优的:holder是MyThreadLocal的静态成员变量,维护的数据是ThreadLocal集合,它不应该将自身数据暴露出去,而是遵循高内聚的设计原则,提供数据操作的能力(方法),例如提供capture的能力,但操作本身需要维护在类内部。因此应该是MyThreadLocal提供capture的能力,然后由需求方(MyTask)进行调用。代码如下

public class MyThreadLocal<T> extends ThreadLocal<T> {

    // ...(省略)

    public static class DataTransmit {

        public static Map<ThreadLocal<Object>, Object> capture() {
            Map<ThreadLocal<Object>, Object> threadLocalValues = new HashMap<>();
            for (MyThreadLocal<Object> threadLocal : holder.get()) {
                threadLocalValues.put(threadLocal, threadLocal.get());
            }
            return threadLocalValues;
        }
    }
}

此处,我在MyThreadLocal中定义了一个内部类DataTransmit,用于ThreadLocal的数据传输,与MyThreadLocal本身提供的能力相隔离(SRP原则)。然后,将原先定义于MyTask的capture方法搬到了DataTransmit类内,提供capture的能力。此时,MyTask构造函数代码如下:

public MyTask(Runnable task) {
    this.task = task;
    threadLocalValues = MyThreadLocal.DataTransmit.capture();
}

我们将capture方法搬走之后,仍然还有replay、restore方法,仔细思考,它们都是对ThreadLocal的操作,放在MyThreadLocal.DataTransmit中更合适一些,使得内聚度更高

最终,MyThreadLocal代码如下

public class MyThreadLocal<T> extends ThreadLocal<T> {

    private static MyThreadLocal<HashSet<MyThreadLocal<Object>>> holder =
            new MyThreadLocal<HashSet<MyThreadLocal<Object>>>() {
                @Override
                protected HashSet<MyThreadLocal<Object>> initialValue() {
                    return new HashSet<>();
                }
            };


    @Override
    public void set(T value) {
        super.set(value);
        addThisToHolder();
    }

    private void addThisToHolder() {
        if (!holder.get().contains(this)) {
            holder.get().add((MyThreadLocal<Object>) this);
        }
    }

    @Override
    public void remove() {
        super.remove();
        removeThisFromHolder();
    }

    private void removeThisFromHolder() {
        holder.get().remove(this);
    }
    public static class DataTransmit {

        public static Map<ThreadLocal<Object>, Object> capture() {
            Map<ThreadLocal<Object>, Object> threadLocalValues = new HashMap<>();
            for (MyThreadLocal<Object> threadLocal : holder.get()) {
                threadLocalValues.put(threadLocal, threadLocal.get());
            }
            return threadLocalValues;
        }

        // 重放,用快照去覆盖当前线程的ThreadLocalMap
        public static Object replay(Object obj) {
            Map<ThreadLocal<Object>, Object> threadLocalValues = (Map<ThreadLocal<Object>, Object>)obj;

            Map<ThreadLocal<Object>, Object> backup = new HashMap<>();
            for (ThreadLocal<Object> threadLocal : threadLocalValues.keySet()) {
                backup.put(threadLocal, threadLocal.get());
            }

            for (Map.Entry<ThreadLocal<Object>, Object> entry : threadLocalValues.entrySet()) {
                ThreadLocal<Object> threadLocal = entry.getKey();
                threadLocal.set(entry.getValue());
            }

            return backup;
        }

        public static void restore(Object obj) {
            Map<ThreadLocal<Object>, Object> backup = (Map<ThreadLocal<Object>, Object>) obj;

            for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
                ThreadLocal<Object> threadLocal = entry.getKey();
                threadLocal.set(entry.getValue());
            }
        }
    }
}

MyTask经过精简后的代码如下:

public class MyTask implements Runnable {
    private Runnable task;
    private final Map<ThreadLocal<Object>, Object> threadLocalValues;

    public MyTask(Runnable task) {
        this.task = task;
        threadLocalValues = MyThreadLocal.DataTransmit.capture();
    }

    @Override
    public void run() {
        Object backup = MyThreadLocal.DataTransmit.replay(threadLocalValues);
        try {
            task.run();
        } finally {
            MyThreadLocal.DataTransmit.restore(backup);
        }
    }
}

使用代码如下:

ExecutorService executorService = Executors.newFixedThreadPool(1);
ThreadLocal<Object> tl1 = new MyThreadLocal<>();
ThreadLocal<Object> tl2 = new MyThreadLocal<>();


// ------------第一次调用 start -------------------
tl1.set("1111");
tl2.set("2222");

executorService.execute(new MyTask(() -> {
    // do biz
    System.out.println(tl1.get());
    System.out.println(tl2.get());
}));

tl1.remove();
tl2.remove();
// ------------第一次调用 end -------------------


// ------------第二次调用 start -------------------
tl1.set("3333");
tl2.set("4444");

executorService.execute(new MyTask(() -> {
    // do biz
    System.out.println(tl1.get());
    System.out.println(tl2.get());
}));

tl1.remove();
tl2.remove();
// ------------第二次调用 end -------------------

这样业务代码就简洁了。 如果还是觉得上述使用姿势有点麻烦:每次提交任务,都要构造一个MyTask,能不能连这一步都省去,变成跟规常的写法一致呢?

executorService.execute(() -> {
    // do biz
    System.out.println(tl1.get());
    System.out.println(tl2.get());
});

答案是肯定的,首先需要明确的一点是,要增强就意味着要代理;接着稍稍转变一下思路:既然不能对Task进行代理,那么我们对线程池进行代理增强,是不是也可以达到同样的效果?

代理套路相信大家很熟悉了:

public class MyExecutorService implements ExecutorService {
    private ExecutorService executorService;

    public MyExecutorService(ExecutorService executorService) {
        this.executorService = executorService;
    }

    @Override
    public void execute(Runnable command) {
        executorService.execute(new MyTask(command)); // 在内部进行Task的代理
    }
    // ...(省略)
}

使用:

ExecutorService executorService = new MyExecutorService(Executors.newFixedThreadPool(1));

这样,业务代码又精简了一步:只需要对线程池进行代理一次即可,后续提交任务不需要手动构建MyTask

最后,如果连对线程池的代理都感觉稍显麻烦,只想使用原生的姿势,那就要请出尚方宝剑:Java Agent,在应用启动之初对JDK代码进行修改,植入代理逻辑,如此业务代码不需要进行其它改动,就享有增强后的ExecutorService以及Task

3.3 alibaba TTL 实现机制

public final void set(T value) {
	if (!disableIgnoreNullValueSemantics && null == value) {
		// may set null to remove value
		remove();
	} else {
		super.set(value);
		addThisToHolder();
	}
}

// 在threadLocal.set() 的时候, 将ThreadLocal存放在这个变量中, 便于后面的重放和恢复
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
	new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
		@Override
		protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
			return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
		}

		@Override
		protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
			return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);
		}
	};


@SuppressWarnings("unchecked")
private void addThisToHolder() {
	if (!holder.get().containsKey(this)) {
		holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
	}
}


// ExecutorServiceTtlWrapper: 对线程池进行包装
public Future<?> submit(@NonNull Runnable task) {
	return executorService.submit(TtlRunnable.get(task, false, idempotent));
}


// 对Runable 进行包装
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
	if (null == runnable) return null;

	if (runnable instanceof TtlEnhanced) {
		// avoid redundant decoration, and ensure idempotency
		if (idempotent) return (TtlRunnable) runnable;
		else throw new IllegalStateException("Already TtlRunnable!");
	}
	return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}

// 类似于我们面对Task的包装方式
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
	// 捕获的数据
	this.capturedRef = new AtomicReference<Object>(capture());
	this.runnable = runnable;
	this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}

/**
 * wrap method {@link Runnable#run()}.
 */
@Override
public void run() {
	// 获取捕获的数据
	final Object captured = capturedRef.get();
	if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
		throw new IllegalStateException("TTL value reference is released after run!");
	}

	// 进行重放数据
	final Object backup = replay(captured);
	try {
		// 执行业务方法
		runnable.run();
	} finally {
		// 重置ThreadLocal
		restore(backup);
	}
}