2014-01-22 27 views
3

我有一些字段管理多个线程可能使用的信息。进入这些领域是至关重要的,所以我实例化一个信号:在Java中测试简单的信号量使用

Semaphore semaphore = new Semaphore(1); 

每个方法都试图获取信号,实质上是在锁定如果没有可用的:

public void method() 
{ 
    semaphore.acquire(); 
    doStruff(); 
    cleanUp() 
} 

//Other methods using the same basic layout as the method above. 

public void cleanUp() 
{ 
    //watch for state consistency and do some clean up 
    semaphore.release(); 
} 

我怎么会去测试上述知道即:

  • 我不想使用定时测试,使用Thread.sleep(x)由于该方法的固有问题,如对时钟的依赖和缺乏对该m的信任ethod。
  • 我不能使用MultithreadedTC,至少不是我所知道的,因为一个线程将始终执行,因此节拍器不会前进到下一个节拍。
  • 我不想修改现有的代码来添加位仅仅用于测试目的。代码应该避开测试中的附加内容。
  • 我想要的东西可以运行多次,并用于回归测试。
  • 因为我不是唯一使用代码的应用程序,它应该是Java本地代码,并且不使用ConAn等脚本语言。

这段代码足够小,我可以很确定地说,它不会死锁或上面详述的条件被违反。但是,一个好的测试案例可以增加它的权重,并且增加更多的信心。

+0

只是一句话:在不同的功能中释放和释放信号量是在寻求麻烦。另外,我不明白为什么当你有一个'lock'的时候使用'semaphore',它更适合这个任务:它将处理优先级反转(如果你的JRE支持它)并且可以被两次同样的线程,如果需要的话。最后,我不明白如何在不添加特定代码的情况下测试关键部分(1)确保发生并发访问尝试(2)避免冲突后受保护数据仍处于一致状态。 –

回答

0

看看JMeter。

它将允许您以多种方式触发代码的多个实例。为了确保您能够看到多线程访问,请考虑在JMeter测试计划中使用多个线程组。

仅供参考:当线程在要更新的实际对象上进行同步时,我发现它很有用。这允许您以受控方式控制多个变量的更新,并且是非常自我记录的。它看起来像这样...

同步(变量) {

}

以上可以出现在几乎任何方法,甚至是构造函数。

+0

每个同步块创建一个与作为参数传递的变量关联的内部锁。如果必须更新一组必须保持一致的对象,则必须手动处理全局锁定以保护整个集合。 –

+0

我建议需要保持一致性的整套值必须在专门创建的类中以保持一致性,并且您要在该类的实例上进行同步。 – Rodney

+0

当然,这将是一种干净的做事方式,但在某些情况下可能会很困难,比如[this](http://stackoverflow.com/questions/21247283/fast-reading-writing-writing-from-to- int-array-concurrently/21298862#21298862)。由于OP没有回应,我不太确定我在那里写的东西的有效性。你怎么看呢? –

1

这是一个测试气味的例子,可以说应该激发代码更改。代码难以测试,因为它在应用程序逻辑责任中混淆了同步责任。一般来说,确保这些代码正确运行是非常困难的。

将同步相关的代码提取到一个单独的类中是最好的,该类本身是可测试的,并使用依赖注入来允许测试来模拟该接口。

例如

public interface ResourceGuard<T> { 
    void accessResource(Consumer<T> consumer); 
} 

public class ExclusiveResourceGuard<T> implements ResourceGuard<T> { 
    private final T instance; 
    private final Semaphore semaphore; 

    public ExclusiveResourceGuard(final T instance) { 
     this.instance = instance; 
    } 

    public void accessResource(Consumer<T> consumer) { 
     semaphore.acquire(); 
     consumer.consume(instance); 
     semaphore.release(); 
    } 
} 

您现在可以通过传递虚假消费作用于测试范围数据测试这个类,并确保进入正确同步。

然后你的生产消费类可以有一个注入的资源保护装置...

public class StuffDoingClass { 
    private final ResourceGuard<MyThing> myThing; 

    public StuffDoingClass(final ResourceGuard<MyThing> theThing) { 
     this.myThing = theThing; 
    } 

    public void method() { 
     this.myThing.accessResource(this::doStuff); 
    } 

    private void doStuff(final MyThing theActualThing) { 
     theActualThing.method(...); 
    } 
} 

现在这个类是可测试与ResourceGuard的一个很简单的模仿。

从设计的角度来看,错误地访问资源也很困难(没有锁定)。我知道这个答案违背了你的“无法更改代码”的要求,但在这种特殊情况下,你似乎不太可能满足你的所有要求。

+0

虽然已经差不多两年了,但我仍有时间重新评估我的设计决定,从“当天回来”!谢谢你的回答,我其实觉得它很优雅。一个快速的问题,但;消费者和Java 1.8的消费者一样? – Eric

+0

是的,我以Java8 Consumer为例。你可以根据自己的用例来替换你想要的任何接口,包括在不使用Java8的情况下使用相同的Guava版本。 – sisyphus