Java 8 具有ForkJoinPool和ThreadLocal的java 8并行流

Java 8 具有ForkJoinPool和ThreadLocal的java 8并行流,java-8,parallel-processing,java-stream,forkjoinpool,inheritable-thread-local,Java 8,Parallel Processing,Java Stream,Forkjoinpool,Inheritable Thread Local,我们使用Java8并行流来处理任务,并通过ForkJoinPool#submit提交任务。我们没有使用jvm范围的ForkJoinPool.commonPool,而是创建自己的自定义池来指定并行性并将其存储为静态变量 我们有一个验证框架,在该框架中,我们将一个表列表置于一个验证器列表之下,并通过自定义ForkJoinPool提交此作业,如下所示: static ForkJoinPool ForkJoinPool=新的ForkJoinPool(4) List tables=tableDAO.fin

我们使用Java8并行流来处理任务,并通过ForkJoinPool#submit提交任务。我们没有使用jvm范围的ForkJoinPool.commonPool,而是创建自己的自定义池来指定并行性并将其存储为静态变量

我们有一个验证框架,在该框架中,我们将一个表列表置于一个验证器列表之下,并通过自定义ForkJoinPool提交此作业,如下所示:

static ForkJoinPool ForkJoinPool=新的ForkJoinPool(4)

List tables=tableDAO.findAll();
ModelValidator validator=验证器工厂
.getInstance().getTableValidator();
列表结果=forkJoinPool.submit(
()->tables.stream()
.parallel()
.map(验证器)
.filter(结果->结果.getValidationMessages().size()>0)
.collect(collector.toList()).get();
我们遇到的问题是,在下游组件中,在静态ForkJoinPool的不同线程上运行的各个验证器依赖于租户id,租户id对于每个请求都不同,并且存储在一个InheritableThreadLocal变量中。由于我们正在创建一个静态ForkJoinPool,ForkJoinPool池中的线程将仅在第一次创建父线程时继承父线程的值。但是这些池线程将不知道当前请求的新租户id。因此,对于后续执行,这些池线程使用旧的租户id

我尝试创建一个自定义ForkJoinPool,在构造函数中指定ForkJoinWorkerThreadFactory,并重写onStart方法以提供新的租户id。但这不起作用,因为onStart方法在创建时只调用一次,而不是在单个执行时调用

似乎我们需要像ThreadPoolExecutor#beforeExecute这样的东西,这在ForkJoinPool中是不可用的。那么,如果要将当前线程本地值传递给静态池线程,我们有什么选择呢

一种解决方法是为每个请求创建ForkJoinPool,而不是使其成为静态的,但我们不想这样做,以避免线程创建的代价高昂


我们有什么选择?

在我看来,最好的选择是去掉本地线程,并将其作为参数传递。但我知道这可能是一项巨大的任务。另一种选择是使用包装器

假设验证器具有验证方法,则可以执行以下操作:

public class WrappingModelValidator implements ModelValidator<Table. ValidationResult> {
    private final ModelValidator<Table. ValidationResult> v;
    private final String tenantId;

    public WrappingModelValidator(ModelValidator<Table. ValidationResult> v, String tenantId) {
        this.v = v;
        this.tenantId = tenantId;
    }

    public ValidationResult validate(Table t) {
      String oldValue = YourThreadLocal.get();
      YourThreadLocal.set(tenantId);
      try {
          return v.validate(t);
      } finally {
          YourThreadLocal.set(oldValue);
      }
    }
}
公共类WrappingModelValidator实现ModelValidator{
私人最终模型验证器v;
私人最终字符串租户;
public WrappingModelValidator(ModelValidator v,String tenantId){
这个,v=v;
this.tenantId=tenantId;
}
公共验证结果验证(表t){
字符串oldValue=YourThreadLocal.get();
YourThreadLocal.set(tenantId);
试一试{
返回v.validate(t);
}最后{
YourThreadLocal.set(oldValue);
}
}
}

然后,您只需包装旧的验证器,它将在条目上设置线程本地,并在完成后将其还原。

我发现以下解决方案可以在不更改任何底层代码的情况下工作。基本上,map方法采用函数接口,我将其表示为lambda表达式。此表达式添加了一个预执行挂钩,用于在当前threadlocal中设置新的tenantId,并在执行后将其清除

       forkJoinPool.submit(tables.stream()
                                 .parallel()
                                 .map((item) -> {
                                    preExecution(tenantId)
                                    try {
                                      return validator.apply(item);
                                    } finally {
                                      postExecution();
                                    }
                                  }   
                                 )
                                 .filter(validationResult -> 
                                   validationResult.getValidationMessages()
                                                   .size() > 0)
                                 .collect(Collectors.toList())).get();
       forkJoinPool.submit(tables.stream()
                                 .parallel()
                                 .map((item) -> {
                                    preExecution(tenantId)
                                    try {
                                      return validator.apply(item);
                                    } finally {
                                      postExecution();
                                    }
                                  }   
                                 )
                                 .filter(validationResult -> 
                                   validationResult.getValidationMessages()
                                                   .size() > 0)
                                 .collect(Collectors.toList())).get();