java IoC框架是如何保证线程安全的?
How does java IoC framework ensure thread safety?
最近看了a great tutorial of Java Memory Model。它说 JVM 只保证
如果未使用同步,final 字段的可见性。然后我想到,当我们使用一些 IoC 框架时,我们通常使用不受 final 语义保护的 setter injection/field 注入。例如,
class SomeController {
private SomeService service;
@Inject
public void setService(SomeService s){
this.service = s;
}
}
是否有可能某些线程在注入后读取 service
的陈旧值?或者我们应该将 service
标记为 volatile 字段?
首先,您正在阅读 确实 过时的“教程”(对于如此复杂的主题来说,这是一个相当奇怪的名称)。此外,该文档针对的是(通常)编写编译器或围绕 JVM 本身工作的人;我仍然觉得它很棒 write-up.
你说得对,在特殊条件下能见度是有保障的;但 final
只是其中之一。至少有3个(不限于):
使用适当的锁定字段
使用静态初始化器
使用 volatile
字段。
最后,这称为“安全发布”,它是关于调用者如何在给定对 SomeController
实例的引用的情况下感知其字段 (service
) 的。他们能保证看到 non-null service
吗?
Spring 保证它将是一个完全初始化的实例,但不是您可能认为的那种感觉。 JLS
里面有个原则叫“happens-before”。它也被称为 happens-before“关系”,因为它涉及两方。例如,一个执行写入(调用 setService
)和一个执行读取(使用 service
)。据说当双方都遵守一些规则时,这种关系就会得到保证和履行(阅读部分看到non-null service
)。这些规则写得非常严格in the JLS。简而言之:当遵循其中一条规则时,您一定会看到 non-null service
只有 。您提到其中之一:
A write to a volatile field happens-before every subsequent read of that field.
但请注意,它不是那里唯一的一个。
因此,如果 Spring,例如,在线程中执行所有注入,并且只有 after 在其上下文中调用 Thread::start
,那么有一条规则 in the JLS here
A call to start() on a thread happens-before any actions in the started thread.
这将保证 service
被注入并正确地被视为 non-null。
这可能需要更多的解释,所以这里有一个例子:
// (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) happens-before (2)
A call to start() on a thread happens-before any actions in the started thread.
这意味着 (2) happens-before (3).
If hb(x, y) and hb(y, z), then hb(x, z).
这意味着 (1) happens-before (3)。这是我们关心的问题,它只是 Spring 实现适当可见性的一种方式。
最近看了a great tutorial of Java Memory Model。它说 JVM 只保证 如果未使用同步,final 字段的可见性。然后我想到,当我们使用一些 IoC 框架时,我们通常使用不受 final 语义保护的 setter injection/field 注入。例如,
class SomeController {
private SomeService service;
@Inject
public void setService(SomeService s){
this.service = s;
}
}
是否有可能某些线程在注入后读取 service
的陈旧值?或者我们应该将 service
标记为 volatile 字段?
首先,您正在阅读 确实 过时的“教程”(对于如此复杂的主题来说,这是一个相当奇怪的名称)。此外,该文档针对的是(通常)编写编译器或围绕 JVM 本身工作的人;我仍然觉得它很棒 write-up.
你说得对,在特殊条件下能见度是有保障的;但 final
只是其中之一。至少有3个(不限于):
使用适当的锁定字段
使用静态初始化器
使用
volatile
字段。
最后,这称为“安全发布”,它是关于调用者如何在给定对 SomeController
实例的引用的情况下感知其字段 (service
) 的。他们能保证看到 non-null service
吗?
Spring 保证它将是一个完全初始化的实例,但不是您可能认为的那种感觉。 JLS
里面有个原则叫“happens-before”。它也被称为 happens-before“关系”,因为它涉及两方。例如,一个执行写入(调用 setService
)和一个执行读取(使用 service
)。据说当双方都遵守一些规则时,这种关系就会得到保证和履行(阅读部分看到non-null service
)。这些规则写得非常严格in the JLS。简而言之:当遵循其中一条规则时,您一定会看到 non-null service
只有 。您提到其中之一:
A write to a volatile field happens-before every subsequent read of that field.
但请注意,它不是那里唯一的一个。
因此,如果 Spring,例如,在线程中执行所有注入,并且只有 after 在其上下文中调用 Thread::start
,那么有一条规则 in the JLS here
A call to start() on a thread happens-before any actions in the started thread.
这将保证 service
被注入并正确地被视为 non-null。
这可能需要更多的解释,所以这里有一个例子:
// (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) happens-before (2)
A call to start() on a thread happens-before any actions in the started thread.
这意味着 (2) happens-before (3).
If hb(x, y) and hb(y, z), then hb(x, z).
这意味着 (1) happens-before (3)。这是我们关心的问题,它只是 Spring 实现适当可见性的一种方式。