package com.laker.admin.framework.ext.thread;

import com.laker.admin.framework.aop.trace.TraceContext;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * <pre>
 * 示例来着：https://blog.csdn.net/qq_40378034/article/details/115272581
 * 因为可继承threadlocal，是在创建子线程时将父线程中的本地变量值复制到子线程，
 *  - 即复制的时机为创建子线程时.
 * 但是当和线程池配合时，线程池有多场景不会创建新线程，而是复用，所以这个例子导致线程本地变量混乱
 * 解决方案
 *  - 1. 使用阿里的TransmittableThreadLocal
 *  - 2. 自己手动从父线程复制到子线程，参考 MDC线程池复制。
 *  结果：
 *  pool-1-thread-3:2
 * pool-1-thread-4:3
 * pool-1-thread-1:0
 * pool-1-thread-7:6
 * pool-1-thread-8:7
 * pool-1-thread-5:4
 * pool-1-thread-6:5
 * pool-1-thread-9:8
 * pool-1-thread-10:9
 * pool-1-thread-2:1
 * ------------这里 线程pool-2-thread-5 复用了，所以都是5
 * parentThreadName:pool-1-thread-6:5  pool-2-thread-5
 * parentThreadName:pool-1-thread-5:5  pool-2-thread-5
 * parentThreadName:pool-1-thread-8:5  pool-2-thread-5
 * parentThreadName:pool-1-thread-3:5  pool-2-thread-5
 * parentThreadName:pool-1-thread-7:5  pool-2-thread-5
 * ------------这里
 * parentThreadName:pool-1-thread-10:9  pool-2-thread-1
 * parentThreadName:pool-1-thread-1:5  pool-2-thread-5
 * parentThreadName:pool-1-thread-4:3  pool-2-thread-2
 * parentThreadName:pool-1-thread-2:1  pool-2-thread-3
 * parentThreadName:pool-1-thread-9:8  pool-2-thread-4
 * </pre>
 */
public class InheritableThreadLocalDemoTraceDefault {
    /**
     * 模拟tomcat线程池
     */
    private static ExecutorService tomcatExecutors = new TraceDefaultThreadPoolExecutor(10, 10, "tomcat");

    /**
     * 业务线程池,默认Control中异步任务执行线程池
     */
    private static ExecutorService businessExecutors = new TraceDefaultThreadPoolExecutor(5, 10, "business");

    /**
     * 线程上下文环境,模拟在Control这一层,设置环境变量,然后在这里提交一个异步任务,模拟在子线程中,是否可以访问到刚设置的环境变量值
     */
    private static ThreadLocal<Integer> requestIdThreadLocal = new InheritableThreadLocal<>();

    /**
     * 模式10个请求,每个请求执行ControlThread的逻辑,其具体实现就是,先输出父线程的名称,
     * 然后设置本地环境变量,并将父线程名称传入到子线程中,在子线程中尝试获取在父线程中的设置的环境变量
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; ++i) {
            tomcatExecutors.submit(new ControlThread(i));
        }

        TimeUnit.SECONDS.sleep(10);
        businessExecutors.shutdown();
        tomcatExecutors.shutdown();
    }

    /**
     * 模拟Control任务
     */
    static class ControlThread implements Runnable {
        private int i;

        public ControlThread(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            TraceContext.addSpan(Thread.currentThread().getName() + ":" + i);
            System.out.println(Thread.currentThread().getName() + ":" + i);
            requestIdThreadLocal.set(i);
            //使用线程池异步处理任务
            businessExecutors.submit(new BusinessTask(Thread.currentThread().getName()));
            TraceContext.stopSpan(-1);

        }
    }

    /**
     * 业务任务,主要是模拟在Control控制层,提交任务到线程池执行
     */
    static class BusinessTask implements Runnable {
        private String parentThreadName;

        public BusinessTask(String parentThreadName) {
            this.parentThreadName = parentThreadName;
        }

        @Override
        public void run() {
            TraceContext.addSpan(parentThreadName);
            //如果与上面的能对应上来,则说明正确,否则失败
            System.out.println("parentThreadName:" + parentThreadName + ":" + requestIdThreadLocal.get() + "  " + Thread.currentThread().getName());
            TraceContext.stopSpan(-1);
        }
    }
}
