我一直在查看一些Google示例代码,它们似乎使用以下代码创建了一个单例:
companion object {
// For Singleton instantiation
@Volatile
private var instance: CarRepository? = null
fun getInstance(carDao: CarDao) =
instance ?: synchronized(this) {
instance ?: CarRepository(carDao).also { instance = it }
}
}
所以我知道@Volatile
意味着
将带注释的属性的JVM支持字段标记为volatile,这意味着对该字段的写入将立即对其他线程可见。
是否应该将所有Singletons实例始终标记为@Volatile
?如果是这样,为什么?
最后,我不了解getInstance
函数
instance ?: synchronized(this) {
instance ?: CarRepository(carDao).also { instance = it }
}
这里到底在做什么?
更新:
来源:Google's Sunflower
我更改了存储库和Dao名称供自己使用,但是在Repository
文件中却是相同的逻辑。
答案 0 :(得分:4)
有一个很好的答案here,说明为什么该字段应该是易变的。本质上,没有它,一个线程可能会在实例完全构建之前获得对该实例的引用。
对于getInstance()
函数,您具有:
instance ?:
这意味着如果该方法不为null,则它将返回instance
,否则将执行?:
的右侧。
synchronized(this) {
instance ?:
}
类似地,在第一次检查实例是否为空之后,在类(companion object
)上同步之后,它再次检查非空值,如果可用,则返回它,然后再执行最后一个命令:
CarRepository(carDao).also { instance = it }
这将初始化一个新的CarRepository
,然后使用.also
块,在返回之前将it
(CarRepository
)分配给instance
字段。仅仅因为整个语句是一个表达式,这有点令人困惑。如果您将其设置得更详细,则可能看起来像这样:
fun getInstance(carDao: CarDao): CarRepository {
var cachedInstance = instance
if (cachedInstance != null) {
return cachedInstance
}
synchronized(this) {
cachedInstance = instance
if (cachedInstance == null) {
cachedInstance = CarRepository(carDao)
instance = cachedInstance
}
return cachedInstance
}
}
值得一提的是,我并不真的相信这个特定的例子是一个很好的模式。例如,考虑以下内容:
val carDao1 = CarDaoImpl1()
val carDao2 = CarDaoImpl2()
val carRepo1 = CarRepository.getInstance(carDao1)
val carRepo2 = CarRepository.getInstance(carDao2)
// carRepo2 actually points to carDao1!
答案 1 :(得分:1)
即使这不是真正的单例,我也将尝试解释注释的确切含义:
fun getInstance(carDao: CarDao) =
/* if the instance is not null, just return it: */
instance ?:
/* instance is null... enter synchronized block for the first thread...
all other threads entering here while the first one is still not finished will block then */
synchronized(this) {
/* now the next line is actually here for all the blocked threads... as soon as they are released, they should take the instance that was set by the first thread */
instance ?:
/* the next line actually is only executed by the first thread entering the synchronized-block */
CarRepository(carDao).also {
/* and this sets the instance that finally is returned by all others */
instance = it }
}
关于@Volatile
...好吧...在这里是这样,以便实例变量实际上可以在随后的线程之间进行同步...以便在第一个线程返回而另一个线程继续进入时可用。同步块。
现在在解释之后:对于Kotlin编写单例的方式,请检查有关Object Expressions, Object Declarations and Companion Objects的Kotlin参考。