多核系统中的最终收集线程是否安全?

时间:2016-12-13 10:30:17

标签: java multithreading thread-safety

我有一个学生问题:

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

我在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();
    }

现在我正在使用Guava的ImmutableMap,我很确定没有问题。但是......

private final Map<ScannerType, ScanExecuter> registry;

如果我取代ImmutableMap只是最终地图(不可变参考但不是内容),是否会出现问题?在多核系统中使用线程缓存,可见性等问题(即使使用singeltons)?

编辑:

  

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

我想解释一下。我对java内存模型不太满意。

我可以更改地图的内容,只在spring配置类中添加新的扫描程序。所以应用程序将在任何情况下重新启动。还有问题吗?

1 个答案:

答案 0 :(得分:2)

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

  • 变量是不可变的或
  • 该变量是可变的,但对该变量的访问是同步的,或
  • 变量是可变的,但在创建(*)后不会更改其值。

如果您将ImmutableMap替换为可变Map(例如HashMap),则在线程之间共享Map之后,final未被静音。 / p>

请注意,将变量定义为final并不能确保其内容永远不会更改。对于对象,final仅表示无法更改引用,但例如可以向Map static添加,删除或更改项目。

(*)注意:正如@bowmore所解释的那样,必须保证safe publication共享变量:

  

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

这可以通过以下方式完成:

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