Ответ 1
Большое спасибо 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
}
}
}