我有一个演员和其他一些对象:
object Config {
val readValueFromConfig() = { //....}
}
class MyActor extends Actor {
val confValue = Config.readValueFromConfig()
val initValue = Future {
val a = confValue // sometimes it's null
val a = Config.readValueFromConfig() //always works well
}
//..........
}
上面的代码是我实际拥有的非常简化的版本。奇怪的是,有时val a = confValue
会返回null
,而如果我将其替换为val a = Config.readValueFromConfig()
,那么它总能正常运作。
我想知道,这是因为与演员互动的唯一方式是发送消息吗?因此,由于val confValue
不是局部变量,我必须使用val a = Config.readValueFromConfig()
(不同的对象,不是演员)或val a = self ! GetConfigValue
并在之后阅读结果?
答案 0 :(得分:2)
我想知道,这是因为与演员互动的唯一方式是发送消息吗?因此,由于val confValue不是局部变量,我必须使用val a = Config.readValueFromConfig()(一个不同的对象,而不是一个actor)
仅仅因为它不是演员,并不意味着它必然是安全的。可能不是。
或val a = self! GetConfigValue并在之后读取结果?
这几乎是正确的。您的意思是self ? GetConfigValue
,我认为 - 会返回Future
,然后您可以map
结束。 !
不返回任何内容。
您无法直接在Future
内读取actor的变量,因为(通常)Future
可以在任何线程上,任何处理器核心上运行,并且您没有任何内存屏障那里强制CPU缓存从主存储器重新加载值。
答案 1 :(得分:2)
val readValueFromConfig() = { //....}
这给了我一个编译错误。我假设你的意思是没有括号?
val readValueFromConfig = { //....}
相同的逻辑与不同的时间给出不同的结果=竞争条件。
val confValue = Config.readValueFromConfig()
始终在构造MyActor
个对象期间执行(因为它是MyActor的一个字段)。有时这会返回null。val a = Config.readValueFromConfig() //always works well
总是稍后执行 - 构建MyActor之后,当Future
initValue
由Executor
执行时。似乎这永远不会返回null。可能的原因:
readValueFromConfig
的身体依赖于另一个人,可以解释一下
并行/异步操作已完成。您是否有机会异步读取配置?鉴于此方法的名称,它可能只是从文件同步读取 - 这意味着这不是原因。Singleton对象不是线程安全的吗?我编译了你的代码。这是你的单例对象java类的反编译:
public final class Config
{
public static String readValueFromConfig()
{
return Config..MODULE$.readValueFromConfig();
}
}
public final class Config$
{
public static final MODULE$;
private final String readValueFromConfig;
static
{
new ();
}
public String readValueFromConfig()
{
return this.readValueFromConfig;
}
private Config$()
{
MODULE$ = this;
this.readValueFromConfig = // ... your logic here;
}
}
Mmmkay ......除非我弄错了,否则这不是线程安全的。
IF 两个线程正在访问readValueFromConfig
(比如说Thread1首先访问它),然后在方法private Config$()
内,MODULE$
在this.readValueFromConfig
之前被不安全地发布设置(引用this
过早地转义构造函数)。紧跟在后面的Thread2可以在设置之前读取MODULE$.readValueFromConfig
。如果'... your logic here
'很慢并阻塞线程,那么极有可能成为问题 - 这正是同步I / O所做的。
故事的道德:避免来自Actors(或任何Threads,包括Executors)的有状态单例对象,或者通过非常谨慎的编码风格使它们变得线程安全。解决方法:更改为def
,在内部缓存私有val
中的值。