Java 多核系统中的最终收集线程安全吗?

Java 多核系统中的最终收集线程安全吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我有一个学生问题: 我想就多核中的可见性问题(如果有)提供一些建议和解释 我在SpringBoot应用程序中使用注册表模式将不同的扫描仪实例映射到它们的名称 配置: @Bean public WebScannerExecutor webScannerExecutor(final WebScannerClientProcessor webScannerClientProcessor) { return new WebScannerExecutor(webScannerClientProce

我有一个学生问题:

我想就多核中的可见性问题(如果有)提供一些建议和解释

我在SpringBoot应用程序中使用注册表模式将不同的扫描仪实例映射到它们的名称

配置:

@Bean
public WebScannerExecutor webScannerExecutor(final WebScannerClientProcessor webScannerClientProcessor) {
    return new WebScannerExecutor(webScannerClientProcessor);
}

@Bean
public TLSScannerExecutor tlsScannerExecutor(final @Value("${tls.scanner.path}") String path, final BashProcessor bashProcessor) {
    return new TLSScannerExecutor(path, bashProcessor);
}

@Bean
public ScanExecuterRegistry executerRegistry(final WebScannerExecutor webScannerExecutor, final TLSScannerExecutor tlsScannerExecutor) {
    final ScanExecuter[] arr = new ScanExecuter[] {webScannerExecutor, tlsScannerExecutor};
    return new ScanExecuterRegistry(arr);
}
public class ScanExecuterRegistry {

private final ImmutableMap<ScannerType, ScanExecuter> registry;

 public ScanExecuterRegistry(final ScanExecuter... executors) {
        this.registry = ImmutableMap.<ScannerType, ScanExecuter> builder().putAll(Arrays.asList(executors).stream().collect(Collectors.toMap(ScanExecuter::getType, e -> e))).build();
    }
注册表:

@Bean
public WebScannerExecutor webScannerExecutor(final WebScannerClientProcessor webScannerClientProcessor) {
    return new WebScannerExecutor(webScannerClientProcessor);
}

@Bean
public TLSScannerExecutor tlsScannerExecutor(final @Value("${tls.scanner.path}") String path, final BashProcessor bashProcessor) {
    return new TLSScannerExecutor(path, bashProcessor);
}

@Bean
public ScanExecuterRegistry executerRegistry(final WebScannerExecutor webScannerExecutor, final TLSScannerExecutor tlsScannerExecutor) {
    final ScanExecuter[] arr = new ScanExecuter[] {webScannerExecutor, tlsScannerExecutor};
    return new ScanExecuterRegistry(arr);
}
public class ScanExecuterRegistry {

private final ImmutableMap<ScannerType, ScanExecuter> registry;

 public ScanExecuterRegistry(final ScanExecuter... executors) {
        this.registry = ImmutableMap.<ScannerType, ScanExecuter> builder().putAll(Arrays.asList(executors).stream().collect(Collectors.toMap(ScanExecuter::getType, e -> e))).build();
    }
公共类ScanExecuterRegistry{
私有最终不可变地图注册表;
公共扫描执行器注册表(最终扫描执行器…执行器){
this.registry=ImmutableMap.builder().putAll(Arrays.asList(executors.stream().collect(Collectors.toMap(ScanExecuter::getType,e->e))).build();
}
现在我正在使用来自番石榴的ImmutableMap,我很确定这没有问题。但是

private final Map<ScannerType, ScanExecuter> registry;
私有最终地图注册;
如果我只使用一个最终的映射(不可变的引用而不是内容)来代替不可变的映射,会不会出现任何问题?多核系统中的线程缓存、可见性等问题(即使使用Singleton)

编辑:

@Bean
public WebScannerExecutor webScannerExecutor(final WebScannerClientProcessor webScannerClientProcessor) {
    return new WebScannerExecutor(webScannerClientProcessor);
}

@Bean
public TLSScannerExecutor tlsScannerExecutor(final @Value("${tls.scanner.path}") String path, final BashProcessor bashProcessor) {
    return new TLSScannerExecutor(path, bashProcessor);
}

@Bean
public ScanExecuterRegistry executerRegistry(final WebScannerExecutor webScannerExecutor, final TLSScannerExecutor tlsScannerExecutor) {
    final ScanExecuter[] arr = new ScanExecuter[] {webScannerExecutor, tlsScannerExecutor};
    return new ScanExecuterRegistry(arr);
}
public class ScanExecuterRegistry {

private final ImmutableMap<ScannerType, ScanExecuter> registry;

 public ScanExecuterRegistry(final ScanExecuter... executors) {
        this.registry = ImmutableMap.<ScannerType, ScanExecuter> builder().putAll(Arrays.asList(executors).stream().collect(Collectors.toMap(ScanExecuter::getType, e -> e))).build();
    }

只要你不修改内容,一切都好

这正是我想要得到解释的。我还不太擅长java内存模型


我只能在spring配置类中添加一个新的扫描程序来更改映射的内容。因此,应用程序在任何情况下都会重新启动。还会有问题吗?

要确保在多线程环境中共享的变量不会导致错误,您需要确保:

  • 变量是不可变的或不可变的
  • 变量是可变的,但对该变量的访问是同步的或不同步的
  • 变量是可变的,但在创建它(*)后不会更改其值
如果将
不可变映射
替换为可变的
映射
(例如
哈希映射
),则在线程之间共享该映射后,如果该映射未被禁用,则所有操作都有效

请注意,将变量定义为
final
并不能确保其内容永远不变。
final
对于对象,仅意味着不能更改引用,但可以向
final
映射添加、删除或更改项


(*)注:如@bowmore所述,有必要保证共享变量的有效性:

安全发布使在发布之前写入的所有值对观察到发布对象的所有读者可见

这可以通过以下方式实现:

  • 静态
    初始值设定项初始化对象引用
  • 将对它的引用存储到
    volatile
    字段中
  • 将对它的引用存储到
    final
    字段中
  • 将对它的引用存储到由(
    synchronized
    )锁正确保护的字段中

为确保多线程环境中共享的变量不会导致错误,您需要确保:

  • 变量是不可变的或不可变的
  • 变量是可变的,但对该变量的访问是同步的或不同步的
  • 变量是可变的,但在创建它(*)后不会更改其值
如果将
不可变映射
替换为可变的
映射
(例如
哈希映射
),则在线程之间共享该映射后,如果该映射未被禁用,则所有操作都有效

请注意,将变量定义为
final
并不能确保其内容永远不变。
final
对于对象,仅意味着不能更改引用,但可以向
final
映射添加、删除或更改项


(*)注:如@bowmore所述,有必要保证共享变量的有效性:

安全发布使在发布之前写入的所有值对观察到发布对象的所有读者可见

这可以通过以下方式实现:

  • 静态
    初始值设定项初始化对象引用
  • 将对它的引用存储到
    volatile
    字段中
  • 将对它的引用存储到
    final
    字段中
  • 将对它的引用存储到由(
    synchronized
    )锁正确保护的字段中

只要你不修改内容,一切都好;只要你不修改内容,比较一切都好;比较好答案,但你还必须确保可变变量安全发布。@鲍莫尔我编辑了我的答案,以展示安全发布可变变量的可能方法。好答案,但你还必须确保可变变量安全发布已安全发布。@bowmore我编辑了答案,以显示安全发布可变变量的可能方法