4 ThreadLocal-TransmittableThreadLocal源码分析

TransmittableThreadLocal
pom引入:

<dependency>

  <groupId>com.alibaba</groupId>
   <artifactId>transmittable-thread-local</artifactId>
   <version>2.10.2</version>
</dependency>

TransmittableThreadLocal源码

继承InheritableThreadLocal

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {}

继承InheritableThreadLocal,说明可以实现父子线程的数据传递。

set方法

@Override
    public final void set(T value) {
        super.set(value);
        // may set null to remove value
        //value设置为null,也可以认为需要移除该TTL。
        if (null == value) removeValue();
        else addValue();
    }

	private void removeValue() {
		//直接移除
        holder.get().remove(this);
    }
    
	private void addValue() {
		//添加TTL,使用WeakHashMap实现Set效果。
        if (!holder.get().containsKey(this)) {
            holder.get().put(this, null); // WeakHashMap supports null value.
        }
    }

holder说明

holder:通过holder持有所有线程的所有TTL变量,这样通过TTL变量,可以获取所有的TTL的值。

	// Note about holder:
    // 1. The value of holder is type Map<TransmittableThreadLocal<?>, ?> (WeakHashMap implementation),
    //    but it is used as *set*.
    // Map虽然是WeakHashMap,但是却是按照set使用。。
    // 2. WeakHashMap support null value.
    // WeakHashMap支持value为null,因此只需要存入null即可,否则还需要创建个Object存储。
    private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
            new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
            	/**提供默认值,减少set时判断,否则还需要判断holder.get()!=null*/
                @Override
                protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
                    return new WeakHashMap<TransmittableThreadLocal<?>, Object>();
                }

				/**重写childValue,表明当前InheritableThreadLocal并没有继承父线程的InheritableThreadLocal*/
                @Override
                protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
                    return new WeakHashMap<TransmittableThreadLocal<?>, Object>(parentValue);
                }
            };

查看holder,跟3 ThreadLocal-手写TransmittableThreadLocal 中的threadLocalsHolder是一样的,同样是持有所有线程的所有TTL变量,通过WeakHashMap实现Set的效果。

get方法

public final T get() {
        T value = super.get();
        if (null != value) addValue();
        return value;
    }
  1. 首先获取父类的get方法,毕竟最终存储还是在InheritableThreadLocalMap中。
  2. 其次判断获取的值是否为null。
    a. 值不为空,添加记录,防止记录丢失。
addValue方法
private void addValue() {
        if (!holder.get().containsKey(this)) {
            holder.get().put(this, null); // WeakHashMap supports null value.
        }
    }

remove方法

	@Override
    public final void remove() {
        removeValue();
        super.remove();
    }
    
	private void removeValue() {
        holder.get().remove(this);
    }
  1. 先移除holder持有的TTL。
  2. 移除InheritableThreadLocal的值。

通过get、set、remove,TLL和自定义的MyTTL类似,只不过在holder存在差别。

TTL使用

public class TTLDemo {
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);
    private static TransmittableThreadLocal currentUser = new TransmittableThreadLocal();

    public static void main(String[] args) {
        currentUser.set("jkf");
        executorService.execute(TtlRunnable.get(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ":" + currentUser.get());
            }
        }));
        executorService.shutdown();
    }
}

输出:
pool-1-thread-1:jkf

TtlRunnable

public final class TtlRunnable implements Runnable, TtlEnhanced {
    private final AtomicReference<Object> capturedRef;
    private final Runnable runnable;
    private final boolean releaseTtlValueReferenceAfterRun;

	private TtlRunnable(@Nonnull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
		//备份业务线程的所有TTL
        this.capturedRef = new AtomicReference<Object>(capture());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }

	@Override
    public void run() {
        Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }
		//恢复业务TTL到池线程,同时返回池线程的TTL备份
        Object backup = replay(captured);
        try {
        	//运行业务
            runnable.run();
        } finally {
        	//复原池线程的TTL
            restore(backup);
        }
    }
}

  1. 实现Runnable,可以提交到线程池。
  2. private final Runnable runnable:实际的业务Runnable,可以对原来的runnable进行包装。
  3. private final AtomicReference capturedRef:业务线程TTL的备份,创建TtlRunnable时,进行快照备份,这样就实现业务线程的TTL备份了。

Transmitter

transmiter搬运工,主要方法:

capture

该方法:直接备份当前线程的所有TTL和vlaue,也可以看做快照。

public static Object capture() {
            Map<TransmittableThreadLocal<?>, Object> captured = new HashMap<TransmittableThreadLocal<?>, Object>();
            //获取当前线程的所有TTL,并快照
            for (TransmittableThreadLocal<?> threadLocal : holder.get().keySet()) {
                captured.put(threadLocal, threadLocal.copyValue());
            }
            return captured;
        }

replay

replay:恢复业务的TTL到池线程中。

public static Object replay(@Nonnull Object captured) {
            @SuppressWarnings("unchecked")
            //已经备份的业务TTL
            Map<TransmittableThreadLocal<?>, Object> capturedMap = (Map<TransmittableThreadLocal<?>, Object>) captured;
            //备份当前线程的TTL
            Map<TransmittableThreadLocal<?>, Object> backup = new HashMap<TransmittableThreadLocal<?>, Object>();
			//循环获取当前线程的所有TTL,并把当前线程打扫赶紧
            for (Iterator<? extends Map.Entry<TransmittableThreadLocal<?>, ?>> iterator = holder.get().entrySet().iterator();
                 iterator.hasNext(); ) {
                Map.Entry<TransmittableThreadLocal<?>, ?> next = iterator.next();
                TransmittableThreadLocal<?> threadLocal = next.getKey();

				//备份当前线程
                // backup 
                backup.put(threadLocal, threadLocal.get());

                // clear the TTL values that is not in captured
                // avoid the extra TTL values after replay when run task
                //移除当前线程其他TTL(业务线程没有的),打扫干净当前线程的TTL
                if (!capturedMap.containsKey(threadLocal)) {
                    iterator.remove();
                    //注意此处会移除InheritableThreadLocalMap的值
                    threadLocal.superRemove();
                }
            }
			// 把业务的TTL,copy到池线程中
            // set values to captured TTL
            setTtlValuesTo(capturedMap);

            // call beforeExecute callback
            doExecuteCallback(true);

            return backup;
        }

private static void setTtlValuesTo(@Nonnull Map<TransmittableThreadLocal<?>, Object> ttlValues) {
            for (Map.Entry<TransmittableThreadLocal<?>, Object> entry : ttlValues.entrySet()) {
                @SuppressWarnings("unchecked")
                TransmittableThreadLocal<Object> threadLocal = (TransmittableThreadLocal<Object>) entry.getKey();
                threadLocal.set(entry.getValue());
            }
        }

  1. 备份池线程的所有TTL。
  2. 清除池线程的其他TTL,打扫干净。
  3. 设置业务TTL到池线程。

restore

public static void restore(@Nonnull Object backup) {
            @SuppressWarnings("unchecked")
            Map<TransmittableThreadLocal<?>, Object> backupMap = (Map<TransmittableThreadLocal<?>, Object>) backup;
            // call afterExecute callback
            doExecuteCallback(false);

            for (Iterator<? extends Map.Entry<TransmittableThreadLocal<?>, ?>> iterator = holder.get().entrySet().iterator();
                 iterator.hasNext(); ) {
                Map.Entry<TransmittableThreadLocal<?>, ?> next = iterator.next();
                TransmittableThreadLocal<?> threadLocal = next.getKey();

                // clear the TTL values that is not in backup
                // avoid the extra TTL values after restore
                //清除不在备份中的TTL,防止出现多余的TTL
                if (!backupMap.containsKey(threadLocal)) {
                    iterator.remove();
                    threadLocal.superRemove();
                }
            }

            // restore TTL values
            //恢复池线程的原来TTL
            setTtlValuesTo(backupMap);
        }

TtlExecutors

因Runnable任务的提交位置比较多,或者TtlRunnable包装不太方便,因此可以考虑从线程池角度处理,对现有的线程池进行增强。

public final class TtlExecutors {
    /**
     * {@link TransmittableThreadLocal} Wrapper of {@link Executor},
     * transmit the {@link TransmittableThreadLocal} from the task submit time of {@link Runnable}
     * to the execution time of {@link Runnable}.
     */
    @Nullable
    public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
        if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) {
            return executorService;
        }
        return new ExecutorServiceTtlWrapper(executorService);
    } 
}


class ExecutorServiceTtlWrapper extends ExecutorTtlWrapper implements ExecutorService, TtlEnhanced {
    private final ExecutorService executorService;

    ExecutorServiceTtlWrapper(@Nonnull ExecutorService executorService) {
        super(executorService);
        this.executorService = executorService;
    }
    //对submit方法进行增强
    public <T> Future<T> submit(@Nonnull Callable<T> task) {
        return executorService.submit(TtlCallable.get(task));
    }
}

class ExecutorTtlWrapper implements Executor, TtlEnhanced {
    private final Executor executor;

    ExecutorTtlWrapper(@Nonnull Executor executor) {
        this.executor = executor;
    }

	/**对execute方法进行增强*/
    @Override
    public void execute(@Nonnull Runnable command) {
        executor.execute(TtlRunnable.get(command));
    }

   
}

上述ExecutorServiceTtlWrapper就是对execute和submit方法进行增强,通过TtlRunnable和TtlCallable,进行包装,一劳永逸的解决ThreadLocal/InheritableThreadLocal切换为TTL。

holder为什么是InheritableThreadLocal

TransmittableThreadLocal的holder是InheritableThreadLocal,而自定义用的是ThreadLocal。

采用InheritableThreadLocal作为holder,主要是解决holder的继承问题,因为如果holder为ThreadLocal,主线程创建子线程,子线程提交任务到线程池时,因为holder的无法传递,导致子线程提交任务时,holder为空。无法备份数据,这样TTL的作用就丧失了。

或者也可以从TransmittanbleThreadLocal宏观说起,TransmittableThreadLocal 继承InheritableThreadLocal,holder使用ThreadLocal,两者的传递性质不同,肯定会导致一个传递了,另一个丢了,类似于:ThreadLocalMap和Thread是同生命周期,而ThreadLocal的生命周期缺不是。

示例:

public class TTLDemo {
    //创建holder
    private static ThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder =
            new ThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
                @Override
                protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
                    return new WeakHashMap<TransmittableThreadLocal<?>, Object>();
                }

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

    public static void main(String[] args) {
        TransmittableThreadLocal ttl = new TransmittableThreadLocal();
        ttl.set("jkf");
        mockSet(ttl);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ":" + ttl.get());
                //类似于TTlRunnable备份,获取当前线程所有的TTL变量,
                //但是因为holder是ThreadLocal,子线程无法获取父线程的holder数据
                //此时备份子线程的ttl变量,就备份了个寂寞
                for (Map.Entry<TransmittableThreadLocal<?>, ?> entry : holder.get().entrySet()) {
                    System.out.println(entry.getKey().get());
                }
                System.out.println("holder的ttl数量:"+holder.get().size());
            }
        }).start();
        while (true) {

        }
    }

    private static void mockSet(TransmittableThreadLocal ttl) {
        WeakHashMap map = new WeakHashMap();
        map.put(ttl, null);
        holder.set(map);
    }
}


输出:
Thread-1:jkf
holder的ttl数量:0

DisableInheritableThreadFactoryWrapper

TTL继承自InheritableThreadLocal,因此InheritableThreadLocal应用于线程池的缺陷,TTL也具有,例如:数据泄漏,当业务线程提交任务时,线程池正好需要创建新线程来执行任务,那么InheritableThreadLocal就会从业务线程,被copy到池线程中,存在一定的数据泄漏。

因为池线程是与业务脱钩的,因此上述情况需要避免。主要通过创建线程时,先移除并备份业务线程的TTL,创建线程,然后再复原业务线程的TTL。

DisableInheritableThreadFactoryWrapper就是解决上述问题。

class DisableInheritableThreadFactoryWrapper implements DisableInheritableThreadFactory {
    final ThreadFactory threadFactory;

    public DisableInheritableThreadFactoryWrapper(@Nonnull ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
    }

    @Override
    public Thread newThread(Runnable r) {
        final Object backup = TransmittableThreadLocal.Transmitter.clear();
        try {
            return threadFactory.newThread(r);
        } finally {
            TransmittableThreadLocal.Transmitter.restore(backup);
        }
    }

    @Nonnull
    @Override
    public ThreadFactory unwrap() {
        return threadFactory;
    }
}
  1. DisableInheritableThreadFactory extends ThreadFactory,因此实现newThread方法。
  2. newThread:
    a. Transmitter.clear():清空所有的业务TTL,同时返回备份。
    b. 调用实际的ThreadFactory创建线程。
    c. 利用备份数据,restore业务线程的TTL。

使用方式

单独setThreadFactory

private static ExecutorService executorService;

    static {
        executorService = Executors.newFixedThreadPool(1);
        ((ThreadPoolExecutor) executorService).setThreadFactory(TtlExecutors.getDisableInheritableThreadFactory(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r);
            }
        }));
    }

单独设置ThreadFactory,创建后立即设置。

ThreadFactory作为入参

new ThreadPoolExecutor(1, 2, 10,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(), TtlExecutors.getDisableInheritableThreadFactory(new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r);
            }
        }));

手动创建线程池时,可以直接指定ThreadFactory。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值