What is a good way to test that a Java method is synchronized?
If you just want to check if a method has the synchronized
modifier, aside from the obvious (looking at the source code/Javadoc), you can also use reflection.
Modifier.isSynchronized(method.getModifiers())
The more general question of testing if a method guarantees proper synchronization in all concurrency scenarios is likely to be an undecidable problem.
These are all horrible ideas, but you could do this...
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
If you know that methods on the arguments are always invoked in any implementation, you can pass a mock object that disguises as the argument and calls holdsLock().
So like:
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(); }
Then wait for the class to invoke something() on Argument.
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.
- The call to join() expects a number of milliseconds, not nanoseconds.
- The two threads must be coordinated, otherwise the attempt thread can start and finish all before the locker thread grabs the lock.
- The attempt thread should not be started until after we record the start time. Otherwise that thread gets enough of a head start that the recorded time can be slightly less than the timeout, causing spurious failures.
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 = {}}
The test looks this:
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 } }}