Что хороший путь состоит в том, чтобы протестировать это, метод Java синхронизируется?

У меня есть несколько классов, которые реализуют некоторый интерфейс. Интерфейс имеет контракт, что должны синхронизироваться некоторые методы, и некоторые не должны, и я хочу проверить что контракт через модульные тесты на все реализации. Методы должны использовать синхронизируемое ключевое слово или быть соединены this - очень похожий на synchronizedCollection () обертка. Это означает, что я должен быть в состоянии наблюдать его внешне.

Для продолжения примера Collections.synchronizedCollection (), если у меня есть один итератор вызова потока () я должен все еще быть в состоянии войти в методы, любят, добавляют () с другим потоком, потому что итератор () не должен делать никакой блокировки. С другой стороны, я должен быть в состоянии синхронизироваться на наборе внешне и видеть, что другой поток блоки на добавляет ().

Существует ли хороший способ протестировать это, метод синхронизируется в тесте JUnit? Я хочу избежать длинных операторов сна.

16
задан Craig P. Motlin 5 March 2010 в 03:55
поделиться

4 ответа

Большое спасибо Цвею Штайнену за описание подхода, который я использовал. В примере кода, над которым я работал, есть несколько проблем, поэтому я подумал, что стоит опубликовать свои выводы здесь.

  • Вызов 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
        }
    }
}
4
ответ дан 30 November 2019 в 22:55
поделиться

Используя отражение, получите объект метода метода и вызовите для него toString (). Ключевое слово synchronized должно появиться в выводе toString ().

1
ответ дан 30 November 2019 в 22:55
поделиться

Если вы просто хотите проверить, есть ли у метода модификатор synchronized , помимо очевидного (глядя на исходный код / ​​документацию Javadoc), вы также можете использовать отражение.

Modifier.isSynchronized(method.getModifiers())

Более общий вопрос тестирования, гарантирует ли метод правильную синхронизацию во всех сценариях параллелизма, вероятно, будет неразрешимой проблемой.

5
ответ дан 30 November 2019 в 22:55
поделиться

Все это ужасные идеи, но вы могли бы сделать это ...

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

Если вы знаете, что методы аргументов всегда вызываются в любой реализации, вы можете передать фиктивный объект, который маскируется под аргумент и вызывает holdLock ().

Примерно так:

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();
    }

Затем подождите, пока класс вызовет something () для аргумента.

4
ответ дан 30 November 2019 в 22:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: