Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
zongchangbo opened this issue Dec 14, 2018 · 8 comments
Closed

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

zongchangbo opened this issue Dec 14, 2018 · 8 comments
Assignees

Comments

@zongchangbo
Copy link

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

@oldratlee
Copy link
Member

oldratlee commented Dec 14, 2018

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

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


简单地说

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

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

image

展开来说

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

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

JDKInheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时ThreadLocal值传递到 任务执行时

讨论和使用注意参见

@oldratlee oldratlee added the ❓question Further information is requested label Dec 14, 2018
@zongchangbo
Copy link
Author

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

@oldratlee oldratlee self-assigned this Dec 19, 2018
@oldratlee
Copy link
Member

oldratlee commented Dec 19, 2018

COOL 解决了就好 ❤️


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

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

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

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

更准确地说, @zongchangbo

  • String是实现成Immutable的,即不可变的。所以父线程不可能看到子线程的变更。
  • String类型的变量,如果你没有做了专门的处理(如使用WeakHashMap / WeakReference之类的),String变量本身是强引用,而非弱引用。

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

@oldratlee oldratlee changed the title 跨线程之间的值传递问题 如何子线程向父线程传递值? Dec 19, 2018
@zongchangbo
Copy link
Author

zongchangbo commented Dec 22, 2018

下面的这个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
Copy link
Author

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

@oldratlee
Copy link
Member

oldratlee commented Feb 11, 2019

@zongchangbo

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

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

@weiliguo15634145
Copy link

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

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

@zongchangbo
Copy link
Author

zongchangbo commented Jul 19, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants