让我们想象我有以下java类:
static class Singleton {
static Singleton i;
static Singleton getInstance() {
if (i == null) {
i = new Singleton();
}
return i;
}
}
现在,我们都知道这会起作用,但是 - 它显然不是线程安全的 - 我实际上并没有尝试修复线程安全 - 这更像是一个演示,我的另一个类是相同的,但是使用了一个互斥体和同步 - 将针对每个运行单元测试以显示一个是线程安全的,而另一个不是。如果getInstance不是线程安全的,那么单元测试会失败的是什么?
答案 0 :(得分:0)
嗯,竞争条件本质上是概率性的,所以没有确定的方法来真正产生竞争条件。任何针对当前代码的可能方法都需要多次运行,直到达到预期的结果。您可以通过使模拟单例进行测试来强制执行对i的访问的松散排序,以模拟某个条件可能看起来像什么。具有同步性的经验法则是预防措施,试图测试并找出代码库中错误代码被破坏后的错误。
static class Singleton {
static Singleton i;
static Singleton getInstance(int tid) {
if (i == null) {
if (tid % 2 == 0) i = new Singleton()
}
return i;
}
}
所以某些线程会写入i,而其他线程会读取i,就像它们已经到达"返回i"之前"偶数线程ID能够检查并初始化i" (有点,不完全是,但它模拟了行为)。尽管如此,在这种情况下,偶数线程之间存在竞争,因为偶数线程在另一个读取null之后仍然可以写入i。为了改进,你需要实现线程安全来强制一个线程读取i的条件,得到null,而另一个线程将i设置为新的Singleton()一个线程不安全的条件。但是在那时你最好只解决潜在的问题(只需使getInstance线程安全!)
TLDR:在不安全的函数调用中可能会出现无限多的竞争条件。您可以模拟代码以生成特定竞争条件的模拟(例如,仅在两个线程之间),但仅仅对竞争条件进行全面测试是不可行的#34;
答案 1 :(得分:0)
这段代码对我有用。 诀窍在于它是其他用户所说的概率。 因此,应该采取的方法是运行多次。
public class SingletonThreadSafety {
public static final int CONCURRENT_THREADS = 4;
private void single() {
// Allocate an array for the singletons
final Singleton[] singleton = new Singleton[CONCURRENT_THREADS];
// Number of threads remaining
final AtomicInteger count = new AtomicInteger(CONCURRENT_THREADS);
// Create the threads
for(int i=0;i<CONCURRENT_THREADS;i++) {
final int l = i; // Capture this value to enter the inner thread class
new Thread() {
public void run() {
singleton[l] = Singleton.getInstance();
count.decrementAndGet();
}
}.start();
}
// Ensure all threads are done
// The sleep(10) is to be somewhat performant, (if just loop,
// this will be a lot slow. We could use some other threading
// classes better, like CountdownLatch or something.)
try { Thread.sleep(10); } catch(InterruptedException ex) { }
while(count.get() >= 1) {
try { Thread.sleep(10); } catch(InterruptedException ex) { }
}
for( int i=0;i<CONCURRENT_THREADS - 1;i++) {
assertTrue(singleton[i] == singleton[i + 1]);
}
}
@Test
public void test() {
for(int i=0;i<1000;i++) {
Singleton.i = null;
single();
System.out.println(i);
}
}
}
这必须在Singleton设计模式中做出一些改变。现在可以在Test类中访问实例变量。这样我们可以在每次重复测试时再次重置null
可用的Singleton实例,然后我们重复测试1000次(如果你有更多的时间,你可以做得更多,有时会发现奇怪的线程问题需要这一点)。
答案 2 :(得分:0)
在某些情况下,此解决方案有效。不幸的是,很难测试单例来引发线程不安全。
while(fileReader.hasNext())
{
text = fileReader.nextLine();
splitText = text.split(:);
}