在Java中测试简单的信号量用法

时间:2014-01-22 10:42:45

标签: java concurrency junit4

我有管理多个线程可能使用的信息的字段。访问这些字段非常重要,因此我实例化了一个信号量:

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)使用{{1}}进行定时测试,因为该方法存在固有问题,例如对时钟的依赖以及缺乏对该方法的信任。
  • 我不能使用MultithreadedTC,至少不是我所知道的,因为一个线程将始终执行,因此,节拍器不会前进到下一个节拍。
  • 我不想修改现有代码以仅为测试目的添加位。代码应该远离测试中的添加内容。
  • 我想要一些可靠的东西,我可以多次运行并用于回归测试。
  • 由于我不是唯一使用该代码的人,因此它应该是Java原生的,而不是使用像ConAn这样的脚本语言。

代码的位数足够小,我可以很好地确定它不会死锁或上面详述的条件被违反。然而,一个好的测试案例可能会给它带来更多的重量,当然会增加信心。

2 个答案:

答案 0 :(得分:1)

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

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

e.g。

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模拟来测试。

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

答案 1 :(得分:0)

看看JMeter。

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

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

同步(变量) {

}

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