java - java IoC框架如何保证线程安全?

标签 java spring concurrency inversion-of-control java-memory-model

最近我读了a great tutorial of Java Memory Model 。它说 JVM 只保证 如果未使用同步,则 final 字段的可见性。然后我突然想到,当我们使用一些 IoC 框架时,我们通常使用不受 final 语义保护的 setter 注入(inject)/字段注入(inject)。例如,

class SomeController {
    private SomeService service;

    @Inject
    public void setService(SomeService s){
        this.service = s;
    }
}

注入(inject)后某个线程是否有可能读取 service 的过时值?或者我们应该将 service 标记为 volatile 字段?

最佳答案

首先,您正在阅读一个非常古老的“教程”(对于如此复杂的主题来说,这是一个相当奇怪的名称)。此外,该文档针对的是(通常)编写编译器或围绕 JVM 本身进行工作的人员;我仍然认为这是一篇很棒的文章。

您是对的,在特殊条件下可以保证可见性;但 final 只是其中之一。至少有3个(且不限于):

  • 使用适当的锁定字段

  • 使用静态初始化器

  • 使用 volatile 字段。

最后,这称为“安全发布”,它是关于调用者如何在给定 SomeController 实例的引用的情况下感知其字段(service) >)。他们能保证看到非空服务吗?

Spring 保证它将是一个完全初始化的实例,但不是您想象的那样。 JLS 中有一个称为“happens-before”的原则。它也被称为“发生在”之前的“关系”,因为它涉及两方。例如,一个执行写入操作(调用 setService),另一个执行读取操作(使用该service)。据说,当双方都遵循某些规则时,这种关系就得到了保证和满足(阅读部分看到一个非空的服务)。这些规则写得非常严格in the JLS 。简而言之:仅当遵循这些规则之一时,您才能保证看到非空服务。您提到了其中之一:

A write to a volatile field happens-before every subsequent read of that field.

但请注意,它并不是唯一的一个。

因此,例如,如果 Spring 在一个线程中执行所有注入(inject),并且仅在其上下文上调用 Thread::start进行,那么就有一个规则in the JLS here

A call to start() on a thread happens-before any actions in the started thread.

这将保证 service 被注入(inject)并正确地视为非空。


这里可能需要更多解释,所以这里是一个例子:

// (1) init Spring context and do the needed injections

// (2) call Thread::start with this context

// (3) use context in a different thread now

我们需要遵循 JLS 文档中的三个规则:

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).

这意味着 (1) 发生在 (2) 之前

A call to start() on a thread happens-before any actions in the started thread.

这意味着 (2) 发生在 (3) 之前。

If hb(x, y) and hb(y, z), then hb(x, z).

这意味着 (1) 发生在 (3) 之前。这就是我们关心的,这只是 Spring 实现适当可见性的一种方式。

关于java - java IoC框架如何保证线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63869205/

相关文章:

java - Spring,新线程中的实例变量可见性从@PostConstruct开始

Java - 如何将字符串转换为货币格式的大十进制

spring - 使用spring-cloud-starter-zuul时如何实现和配置route类型的ZuulFilter?

java - 另一个 PHP JSONException : Value <br of type java. lang.String 无法转换为 JSONObject

java - Spring框架和tomcat - 如何启用以下文件夹符号链接(symbolic link)?

java - Spring上下文配置文件问题

Java CompletableFuture thenCompose 与几个异步任务

Java Executor 在上一个 Callable 完成后执行一个 Callable?

java - OSX 中使用智能卡和 BC 的数字签名

java - SQLite查询后光标返回无结果(通过单元测试但在运行时失败)--android