Java 8 并行流和 ThreadLocal

2022-09-03 07:15:22

我正在尝试弄清楚如何在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

而不是第一个?


答案 1

正如 Louis 在注释中所述,您的示例可以很好地简化为在 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

在您的例子中,声明为 一个 ,因为在 中创建的实例是懒惰地创建的,因此它们都将从方法(和线程)中的值继承。valInheritableThreadLocalThreadparallel()ForkJoinPool#commonPool()setmain

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


答案 2

推荐