alibaba / transmittable-thread-local

📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.
https://github.com/alibaba/transmittable-thread-local
Apache License 2.0
7.59k stars 1.69k forks source link

如何子线程向父线程传递值? #125

Closed zongchangbo closed 5 years ago

zongchangbo commented 5 years ago

目前发现的都是主线程到子线程的 值传递; 那从子线程到主线程的 却不能实现,这个有办法解决吗?

oldratlee commented 5 years ago

目前发现的都是主线程到子线程的 值传递; 那从子线程到主线程的 却不能实现

你的需求场景是和要解决的问题是什么,可以先具体些 说明一下。 😄 @zongchangbo


简单地说

TTL缺省向子线程传递的是引用,即子线程改了,父线程是可见的 。

这即是 支持了 子线程向父线程 传递,注意线程安全。

image

展开来说

  1. 任何支持多线程的环境都 支持 从不同线程之间 『传递』(或说共享)信息。 比如通过队列、通过静态全局变量。
  2. 如果 是可变的(即信息 变成了 状态),『共享状态』要注意同步 !即 平时 大家说的 并发编程/线程安全。
  3. TransmittableThreadLocal提供的是 一个 典型场景下的规范化/模式化的解法,进而 简化 使用、实现和系统设计。

关于『TTL 目标/功能/典型场景』参见文档:

讨论和使用注意参见

zongchangbo commented 5 years ago

嗯 谢谢啊,TTL缺省向子线程传递的是引用, 而我之前用的是String 这种弱引用类型 ,所以失败了

oldratlee commented 5 years ago

COOL 解决了就好 ❤️


能说明一下你的需求场景/使用场景吗? @zongchangbo ❤️

方便 的话,请记到 Issue 【用户反馈收集】说一下您的使用场景、使用项目、公司,感谢! 中 :)

PS 关于String类型(Immutable) 与 强弱引用

之前用的是String这种弱引用类型 ,所以失败了

更准确地说, @zongchangbo

WeakReference 中,有关于弱引用的解释。

zongchangbo commented 5 years ago

下面的这个demo是可以的:

public class CustomThreadLocal {
    static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                CustomThreadLocal.threadLocal.set("test: " + new Random(10).nextInt());
                new Service().call();
            }
        }).start();
    }
}

class Service {
    public void call() {
        //System.out.println("Service:" + Thread.currentThread().getName());
        System.out.println("Service:" + CustomThreadLocal.threadLocal.get());
        new Dao().call();
    }
}

class Dao {
    public void call() {
        System.out.println("==========================");
       // System.out.println("Dao:" + Thread.currentThread().getName());
        System.out.println("Dao:" + CustomThreadLocal.threadLocal.get());
    }
}

但是我实际工作中的不行

@Slf4j
public class PrometheusMetricsInterceptor extends HandlerInterceptorAdapter {

    static final Counter requestCounter = Counter.build()
            .name("cuishoufen_http_requests_total").labelNames("path", "method", "code")
            .help("Total requests.").register();

    public static final TransmittableThreadLocal<String> localStatus = new TransmittableThreadLocal<String>();

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        int status = response.getStatus();
        requestCounter.labels(requestURI, method, localStatus.get() == null ? String.valueOf(status): localStatus.get()).inc();
        localStatus.remove();
        super.afterCompletion(request, response, handler, ex);
    }

    /////////////////////////////////////////////////////////

    @HystrixCommand(
            fallbackMethod = "getCuishouTag_fallback",
            groupKey = "getCuishouTag",
            commandKey = "getCuishouTag",
            threadPoolKey = "getCuishouTag",
            commandProperties = {
                    //设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,并执行回退逻辑
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "200"),
                    //设置在一个滚动窗口中,打开断路器的最少请求数
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "4"),
                    //设置在回路被打开,拒绝请求到再次尝试请求并决定回路是否继续打开的时间
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "60000"),
                    //设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000")
            },
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),
                    @HystrixProperty(name = "maxQueueSize", value = "200"),
                    //设置队列拒绝的阈值——一个人为设置的拒绝访问的最大队列值,即使maxQueueSize还没有达到
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "50"),
                    //设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "180000")
            }
    )
    @Override
    public HashMap<String, String> getCuishouTag(String tel) {
        HashMap<String,String> hm = new HashMap<String,String>();
        try {
            String param = buildParam(tel);
            String response = getResponse(URL_2, param);
            if (StringUtils.isNotBlank(response)) {
                hm = parseResponse(response);
            }
        }catch (Exception e){
            PrometheusMetricsInterceptor.localStatus.set("9999")
            log.error("getCuishouTag process error, tel={}", tel, e);
            return hm;
        }
        return hm;
    }
}
zongchangbo commented 5 years ago

然后我的解决方案就是把String 换成一个对象,请问为何会发生这样的事情?

oldratlee commented 5 years ago

@zongchangbo

因为 涉及 HandlerInterceptorAdapter/HystrixCommand ,要具体分析这些涉及框架的 整体流程 与 代码实现。

这些 框架 我没有具体用过、不熟悉了解,所以 你要自己去分析一下了。 😄

weiliguo15634145 commented 2 years ago

目前发现的都是主线程到子线程的 值传递; 那从子线程到主线程的 却不能实现,这个有办法解决吗?

解决了吗? 我现在也遇到了同样的问题。我有一个需求是收集所有子线程的执行日志然后到父线程汇总 。

zongchangbo commented 2 years ago

微信说吧,809116583

------------------ 原始邮件 ------------------ 发件人: "alibaba/transmittable-thread-local" @.>; 发送时间: 2022年7月19日(星期二) 晚上6:28 @.>; @.**@.>; 主题: Re: [alibaba/transmittable-thread-local] 如何子线程向父线程传递值? (#125)

目前发现的都是主线程到子线程的 值传递; 那从子线程到主线程的 却不能实现,这个有办法解决吗?

解决了吗? 我现在也遇到了同样的问题。我有一个需求是收集所有子线程的执行日志然后到父线程汇总 。

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>