What is a good way to test that a Java method is synchronized?

前端 未结 4 412
野趣味
野趣味 2020-12-10 02:29

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

相关标签:
4条回答
  • 2020-12-10 03:09

    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
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-10 03:12

    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.

    0 讨论(0)
  • 2020-12-10 03:23

    Using reflection, get the method's Method object, and invoke toString() on it. The "synchronized" keyword should appear in toString()'s output.

    0 讨论(0)
  • 2020-12-10 03:30

    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.

    0 讨论(0)
提交回复
热议问题