【问题标题】:Java 8 parallel stream and ThreadLocalJava 8 并行流和 ThreadLocal
【发布时间】:2016-02-28 20:22:51
【问题描述】:

我想弄清楚如何在 Java 8 并行流中复制 ThreadLocal 值。

如果我们考虑这个:

    public class ThreadLocalTest {

        public static void main(String[] args)  {
            ThreadContext.set("MAIN");
            System.out.printf("Main Thread: %s\n", ThreadContext.get());

            IntStream.range(0,8).boxed().parallel().forEach(n -> {
                System.out.printf("Parallel Consumer - %d: %s\n", n, ThreadContext.get());
            });
        }

        private static class ThreadContext {
            private static ThreadLocal<String> val = ThreadLocal.withInitial(() -> "empty");

            public ThreadContext() {
            }

            public static String get() {
                return val.get();
            }

            public static void set(String x) {
                ThreadContext.val.set(x);
            }
        }
    }

哪些输出

Main Thread: MAIN
Parallel Consumer - 5: MAIN
Parallel Consumer - 4: MAIN
Parallel Consumer - 7: empty
Parallel Consumer - 3: empty
Parallel Consumer - 1: empty
Parallel Consumer - 6: empty
Parallel Consumer - 2: empty
Parallel Consumer - 0: MAIN

有没有办法让我将 ThreadLocal 从 main() 方法克隆到为每个并行执行产生的线程中?

这样我的结果是:

Main Thread: MAIN
Parallel Consumer - 5: MAIN
Parallel Consumer - 4: MAIN
Parallel Consumer - 7: MAIN
Parallel Consumer - 3: MAIN
Parallel Consumer - 1: MAIN
Parallel Consumer - 6: MAIN
Parallel Consumer - 2: MAIN
Parallel Consumer - 0: MAIN

而不是第一个?

【问题讨论】:

  • 我没想到会这样。但是为什么首先是 ThreadLocal,而不是在你的 main 方法中创建然后显式传递给 lambda?
  • 你想越过溪流吗?我听说那会很糟糕。

标签: java java-8 threadpool forkjoinpool


【解决方案1】:

As Louis has stated in the comments,您的示例可以很好地简化为在 lambda 表达式中捕获局部变量的值

public static void main(String[] args)  {
    String value = "MAIN";
    System.out.printf("Main Thread: %s\n", value);

    IntStream.range(0,8).boxed().parallel().forEach(n -> {
        System.out.printf("Parallel Consumer - %d: %s\n", n, value);
    });
}

从您的示例中看不出完整的用例是什么。

如果您确切知道哪些线程将从您的主线程启动,您可以考虑使用InheritableThreadLocal

此类扩展 ThreadLocal 以提供 值的继承 父线程到子线程:创建子线程时, child 接收所有可继承线程本地的初始值 父级具有值的变量。

在您的情况下,将val 声明为InheritableThreadLocal,因为在ForkJoinPool#commonPool() 中为parallel() 创建的Thread 实例是延迟创建的,它们都将从@ 中的值set 继承987654332@ 方法(和线程)。

如果您在原始线程中设置InhertiableThreadLocal 值之前以某种方式使用了commonPool(或调用parallel 终端操作的任何池),则不会出现这种情况。

【讨论】:

  • 是的,我的示例没有提供太多关于用例的信息。实际用例与其他一些代码(除了 parallel() 消费者)一起使用,ThreadLocal 被填充到 Servlet 过滤器中,然后在 Spring MVC 控制器中使用,并且有时被克隆用于全面的各种可运行实现(服务调用等)。所以接下来会有各种各样的调用,它们是 ThreadContext.get(something)。感谢您解释 InheritableThreadLocal,我会尝试一下!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-24
  • 1970-01-01
  • 2016-08-27
  • 2018-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多