测试Java方法是否同步的好方法是什么?

我有几个实现一些接口的类。 接口有一个契约,有些方法应该同步,有些不应该,我想通过unit testing来validation所有实现的合同。 这些方法应该使用synchronized关键字或者锁定this – 非常类似于synchronizedCollection()包装器。 这意味着我应该能够在外部观察它。

要继续Collections.synchronizedCollection()的示例,如果我有一个线程调用iterator(),我仍然可以使用另一个线程进入add()等方法,因为iterator()不应该执行任何锁定。 另一方面,我应该能够在外部同步集合,并看到另一个线程阻塞add()。

有没有一种方法可以测试方法是否在JUnit测试中同步? 我想避免长时间的睡眠陈述。

如果你只是想检查方法是否具有synchronized修饰符,除了显而易见的(查看源代码/ Javadoc),你也可以使用reflection。

 Modifier.isSynchronized(method.getModifiers()) 

测试方法是否保证在所有并发方案中正确同步的更一般的问题可能是一个不可判定的问题。

这些都是可怕的想法,但你可以这样做……

1

  // Substitute this LOCK with your monitor (could be you object you are // testing etc.) final Object LOCK = new Object(); Thread locker = new Thread() { @Override public void run() { synchronized (LOCK) { try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { System.out.println("Interrupted."); return; } } } }; locker.start(); Thread attempt = new Thread() { @Override public void run() { // Do your test. } }; attempt.start(); try { long longEnough = 3000 * 1000;// It's in nano seconds long before = System.nanoTime(); attempt.join(longEnough); long after = System.nanoTime(); if (after - before < longEnough) { throw new AssertionError("FAIL"); } else { System.out.println("PASS"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } locker.interrupt(); 

2

如果您知道在任何实现中始终调用参数上的方法,则可以传递伪装成参数的模拟对象并调用holdsLock()。

所以喜欢:

 class Mock implements Argument { private final Object LOCK; private final Argument real; public Mock(Object obj, Argument real){ this.LOCK=obj; this.real = real; } @Overrides public void something(){ System.out.println("held:"+Thread.holdsLock(LOCK)); this.real.something(); } 

然后等待类在Argument上调用something()。

非常感谢Zwei steinen撰写我使用过的方法。 我完成的示例代码中存在一些问题,因此我认为值得在此处发布我的发现。

  • 对join()的调用需要几毫秒,而不是纳秒。
  • 必须协调两个线程,否则尝试线程可以在锁定器线程抓取锁之前开始和完成所有线程。
  • 在记录开始时间之前,不应启动尝试线程。 否则该线程获得足够的开头时间,记录的时间可能略小于超时,从而导致虚假失败。

以下是Scala特征的同步测试代码:

 trait SynchronizedTestTrait { val classUnderTest: AnyRef class Gate { val latch = new java.util.concurrent.CountDownLatch(1) def open() { this.latch.countDown } def await() { this.latch.await } } def nanoTime(code: => Unit) = { val before = System.nanoTime code val after = System.nanoTime after - before } def assertSynchronized(code: => Unit) { this.assertThreadSafety(threadSafe = true, millisTimeout = 10L)(code) } def assertNotSynchronized(code: => Unit) { this.assertThreadSafety(threadSafe = false, millisTimeout = 60L * 1000L)(code) } def assertThreadSafety(threadSafe: Boolean, millisTimeout: Long)(code: => Unit) { def spawn(code: => Unit) = { val result = new Thread { override def run = code } result.start() result } val gate = new Gate val lockHolderThread = spawn { this.classUnderTest.synchronized { // Don't let the other thread start until we've got the lock gate.open() // Hold the lock until interruption try { Thread.sleep(java.lang.Long.MAX_VALUE) } catch { case ignore: InterruptedException => return; } } } val measuredNanoTime = nanoTime { // Don't start until the other thread is synchronized on classUnderTest gate.await() spawn(code).join(millisTimeout, 0) } val nanoTimeout = millisTimeout * 1000L * 1000L Assert.assertEquals( "Measured " + measuredNanoTime + " ns but timeout was " + nanoTimeout + " ns.", threadSafe, measuredNanoTime > nanoTimeout) lockHolderThread.interrupt lockHolderThread.join } } 

现在让我们说我们想测试一个简单的类:

 class MySynchronized { def synch = this.synchronized{} def unsynch = {} } 

测试看起来像这样:

 class MySynchronizedTest extends SynchronizedTestTrait { val classUnderTest = new MySynchronized @Test def synch_is_synchronized { this.assertSynchronized { this.classUnderTest.synch } } @Test def unsynch_not_synchronized { this.assertNotSynchronized { this.classUnderTest.unsynch } } } 

使用reflection,获取方法的Method对象,并在其上调用toString()。 “synchronized”关键字应出现在toString()的输出中。