在Brian的练习书中阅读Java并发性时,我遇到了数据竞赛和种族条件。
数据竞赛
当有一个变量被多个线程读取,由至少一个线程写入,并且写入和读取不按发生顺序排列时,程序被称为有数据竞争,因此不是一个“适当同步”的程序。
种族条件
当计算的正确性取决于运行时多个线程的相对定时或交织时,就会发生争用状态;换句话说,得到正确的答案取决于幸运的时间。最常见的比赛条件是“先检查后行动”,其中一个潜在的陈腐的观察被用来决定下一步该做什么。
据我所理解,可以通过确保上述一个或多个条件中的一个或多个保持为false --即,通过使共享变量不可变,或者通过适当地对它们进行synchronized访问,来避免数据竞争。
我的问题是关于一个SingletonFactory的例子,这个例子通常是用来说明竞赛条件的。
例如:
public class SingletonFactory {
private Singleton singleton = null;
private SingletonFactory() {}
public Singleton getInstance() {
if(this.singleton == null) {
this.singleton = new Singleton();
}
return this.singleton;
}
}这段代码还能被认为是导致数据竞争的吗?
我知道,使上述程序“完全线程安全”的一种方法是拥有一个二次检查锁定,并使类变量volatile。
但是,如果我只是声明Singleton变量volatile,但未能同步初始化变量的代码块,那么它是否可以被认为是安全的--至少w.r.t“数据竞赛”,但仍然不安全。种族状况?一般来说,我仍然在寻找一个好的现实例子,其中没有数据竞赛,但仍然有一个潜在的竞赛条件!
(通常用来解释数据争用和争用条件之间的区别的博客不能帮助我理解这一点)
发布于 2014-02-17 13:24:29
有三种一般的口味,这是常见的懒惰单例。第一个例子是没有同步的,就像第一个例子一样。第二个,正如您所提到的,是双重检查锁定,没有易失性,最后是DCL与易失性。一个包含竞争条件(如果共享字段是同步的)和一个包含数据竞争。
public static class Singleton{
private static volatile Singleton INSTANCE; // volatile to illustrate the race condition
public static Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}在这种情况下,不存在数据竞争,但是存在一个竞争条件。这里的竞争是两个或多个线程可以创建一个Singleton实例。
下面是一个双重检查的锁定示例:
public static class Singleton{
private static Singleton INSTANCE; // not volatile to illustrate the data-race
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}在本例中,是数据争用,而不是争用条件。尽管实例不是null,但其他线程可能看不到实例变量发生的写入。
所以回答你的问题。
我的问题是,这个代码是否也可以被认为是导致数据竞争的原因?
在您的示例中,它包含一个数据竞赛和一个竞赛条件,因为共享可变变量既不同步,也不同步检查-然后集的原子操作。
发布于 2014-02-17 13:21:52
嗯,不,它不会是数据安全的,因为两个调用者仍然可以得到两个不同的“单例”实例。
数据种族是-一个种族条件,但并非所有的种族条件是数据种族。
还可能发生两个并行执行路径竞争一个结果。考虑信号-发送SIGHUP和SIGTERM到一个和相同的进程。会发生什么呢?按什么顺序?即使在执行过程中没有显式共享数据,这种行为(通常)也是不确定的。
https://stackoverflow.com/questions/21829921
复制相似问题