dre*_*ash 7 java multithreading synchronization thread-safety
考虑以下代码:
private static Singleton singleton;
public static Singleton get(){
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
return singleton; // <-- this part is important
}
Run Code Online (Sandbox Code Playgroud)
这是这个问题的后续讨论。最初,我认为它是线程安全的。然而,一些受人尊敬的用户认为,由于块的return singleton外部,这不是线程安全的synchronized。然而,其他一些(受人尊敬的)用户则反对。
在我阅读了do we need volatile when implementations singleton using double-check locked 之后,我改变了主意。(来自那个问题的代码):
private static Singleton instance;
private static Object lock = new Object();
public static Singleton getInstance() {
if(instance == null) {
synchronized (lock) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
Run Code Online (Sandbox Code Playgroud)
(众所周知,为什么volatile第二个代码需要 。)
但是,在再次查看这两个示例后,我注意到第一个和第二个代码片段之间存在很大差异。在前者中,最外层if是在synchronized子句内,因此在synchronized块内运行的所有线程都将强制发生先发生关系(即,null如果实例设置正确,线程将无法返回)或者我错了?我希望采取以下行动顺序:
lock monitor
...
unlock monitor
...
read singleton
Run Code Online (Sandbox Code Playgroud)
我注意到所有与第一个代码片段相似的在线示例在synchronized块内都有返回;然而,这可能仅仅是因为在性能方面它是相同的,因为线程必须同步离开,所以为什么不安全起见并将返回值放在里面呢?!.
题:
返回真的需要在synchronized块内吗?return 语句的单例值的读取能否在synchronized块开始之前看到单例值?
返回值真的需要位于同步块内吗?
否,return不需要位于synchronized块中,除非该singleton字段可以分配到其他地方。但是,没有充分的理由说明为什么return不应位于同步块内。如果整个方法都包装在同步中,那么如果我们在此处的类中,则可以将该方法标记为同步Singleton。如果单例在其他地方被修改,这会更干净、更好。
就为什么它不需要在内部而言,因为您使用的是synchronized块,所以在块的开头有一个读屏障,在末尾有一个写屏障,这意味着线程将获得最多的资源的最新值singleton并且只会被分配一次。
读取内存屏障确保线程将看到更新的单例,该单例将是null或完全发布的对象。写内存屏障确保任何更新都singleton将写入主内存,其中包括完整构建Singleton并将其发布到字段singleton。程序顺序保证块singleton内分配的synchronized值将作为相同的值返回,除非另一个线程中存在另一个分配,否则singleton它将是未定义的。
如果您执行以下操作,程序顺序将更加有效。我倾向于在(使用适当的双重检查锁定代码)时执行此singleton操作volatile。
synchronized (Singleton.class) {
Singleton value = singleton;
if (singleton == null) {
value = new Singleton();
singleton = value;
}
return value;
}
Run Code Online (Sandbox Code Playgroud)
不是线程安全的,因为返回单例位于同步块之外
由于您使用的是synchronized块,所以这不是问题。正如您所指出的,双重检查锁定就是为了避免synchronized每次操作时都碰到块。
在同步块中运行的所有线程将强制发生先于关系(即,如果正确设置了实例,线程将无法返回 null)还是我错了?
这是正确的。你没有错。
然而,这可能只是因为性能方面它是相同的,因为线程必须同步,所以为什么不为了安全起见并将返回值放在里面呢?!
没有理由不这样做,尽管我认为“安全方面”更多的是在其他人审查此代码并在将来担心它时引起恐慌,而不是从语言定义的角度来看“更安全”。同样,如果还有其他地方singleton被分配,那么return应该在块的内部synchronized。