测试 Java 方法是否同步的好方法是什么?
我有几个类实现了一些接口.该接口有一个合同,一些方法应该同步,而有些方法不应该同步,我想通过所有实现的单元测试来验证该合同.这些方法应该使用 synchronized 关键字或锁定在 this
- 非常类似于 synchronizedCollection() 包装器.这意味着我应该能够从外部观察它.
I have several classes that implement some interface. The interface has a contract, that some methods should be synchronized, and some should not, and I want to verify that contract through unit tests for all the implementations. The methods should use the synchronized keyword or be locked on this
- very similar to the synchronizedCollection() wrapper. That means I should be able to observe it externally.
继续Collections.synchronizedCollection() 如果我有一个线程调用 iterator(),我应该仍然能够使用另一个线程进入 add() 之类的方法,因为 iterator() 不应该进行任何锁定.另一方面,我应该能够在外部同步集合,并看到 add() 上的另一个线程阻塞.
To continue the example of Collections.synchronizedCollection() if I have one thread calling iterator(), I should still be able to get into methods like add() with another thread because iterator() should not do any locking. On the other hand, I should be able to synchronize on the collection externally and see that another thread blocks on add().
有没有一种好方法可以测试 JUnit 测试中的方法是否同步?我想避免长时间的睡眠陈述.
推荐答案
非常感谢 Zwei steinen 编写了我使用的方法.我处理的示例代码中存在一些问题,所以我认为值得在这里发布我的发现.
A big thank you to Zwei steinen for writing up the approach I used. There are a few problems in the example code that I worked through, so I thought it would be worth posting my findings here.
- 对 join() 的调用需要毫秒数,而不是纳秒.
- 两个线程必须协调,否则尝试线程可以在locker线程抢到锁之前开始和结束.
- 在我们记录开始时间之前,不应启动尝试线程.否则,该线程会获得足够的领先优势,以至于记录的时间可能会略小于超时,从而导致虚假故障.
这是作为 Scala trait 的同步测试代码:
Here is the synchronization test code as a Scala trait:
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
}
}
现在假设我们要测试一个简单的类:
Now let's say we want to test a simple class:
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
}
}
}
相关文章