2013-10-02 6 views
4

Согласно книге Java Параллелизм на практике в листинге 12.3 мы могли протестировать параллельный код, используя следующий код:Тестирование параллелизм в Java

void testTakeBlocksWhenEmpty() { 
final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10); 
Thread taker = new Thread() { 
    public void run() { 
    try { 
    int unused = bb.take(); 
    fail(); // if we get here, it’s an error 
    } catch (InterruptedException success) { } 
    } 
}; 
try { 
    taker.start(); 
    Thread.sleep(LOCKUP_DETECT_TIMEOUT); 
    taker.interrupt(); 
    taker.join(LOCKUP_DETECT_TIMEOUT); 
    assertFalse(taker.isAlive()); 
} catch (Exception unexpected) { 
    fail(); 
} 
} 

Допустим, что следующие шаги выполняются:

  1. taker нить началась.
  2. bb.take() вернулся успешно, и мы просто немного перед запуском метода fail().
  3. Метод называется interrupt().
  4. Мы находимся в блоке catchtaker.

Итак, мы находимся в блоке catch на данный момент, но на самом деле метод проверки не прошел. Это не удается, и мы никогда не информированы.

Это право? Если да, то как мы можем это исправить?

+0

Никогда не видел структуру данных BoundedBuffer ранее в Java. Что делает функция()? Пожалуйста, предоставьте несколько ссылок. –

+0

«Thread.sleep» будет близок к уверенности в том, что если 'take'does не блокируется, метод' fail' будет вызываться до того, как поток будет прерван. И вообще: прерывание просто устанавливает флаг. Вам нужно будет заблокировать, чтобы исключение было выбрано. – Fildor

+0

@Fildor: Допустим, что 'LOCKUP_DETECT_TIMEOUT' недостаточно. Должен ли я увеличить этот раз? Правильно ли это, или я должен что-то изменить в коде? – LiTTle

ответ

0

take предполагается заблокировать в пустой очереди. Таким образом, ожидаемая последовательность событий:

  • taker.start(); => запустить поток
  • Thread.sleep(LOCKUP_DETECT_TIMEOUT); ждать, чтобы убедиться, что поток запускается и take был вызван. Фактическое значение константы трудно оценить, но чего-то большего, чем несколько сотен миллисов, должно быть достаточно - в качестве альтернативы вы можете использовать CountDownLatch, чтобы знать, когда запускается нить нити.
  • в ответной теме: bb.take(); => блок - если он не fail() называется и тест не
  • в основном потоке: taker.interrupt(); => метод take() должен выйти с InterruptedException
  • в основном потоке: taker.join(); => подождать некоторое время, чтобы позволить
  • в основной теме: assertFalse(taker.isAlive()); => подтвердить, что принимающая сторона нить вышла и не заблокирована в методе take больше

Исполнения с защелкой (предполагается, что если поток прерывается перед темtake называется, take выйдет с InterruptedException - если нет, то вам нет другого пути, кроме как добавить некоторый случайный сон перед вызовом started.await()):

void testTakeBlocksWhenEmpty() { 
final CountDownLatch started = new CountDownLatch(1); 
final CountDownLatch ended = new CountDownLatch(1); 
final BoundedBuffer<Integer> bb = new BoundedBuffer<Integer>(10); 
Thread taker = new Thread() { 
    public void run() { 
    try { 
    started.countDown(); 
    int unused = bb.take(); 
    fail(); // if we get here, it’s an error 
    } catch (InterruptedException success) { } 
    ended.countDown(); 
    } 
}; 
try { 
    taker.start(); 
    started.await(); 
    taker.interrupt(); 
    assertTrue(ended.await()); 
} catch (Exception unexpected) { 
    fail(); 
} 
} 

Вы должны добавить тайм-аут для вашего метода испытаний или защелки (достаточно долго, чтобы не мешать, если тест проходит, например, через 5 секунд). Это позволит избежать блокировки всего набора тестов.

0

Общий подход для тестирования потока/асинхронного кода состоит в том, чтобы блокировать основной тестовый поток, захватывать неудачные утверждения из других потоков, перезапускать основной тестовый поток и реконструировать любые сбои. Это довольно шаблонный, который можно сделать вручную, где удобна библиотека, например, ConcurrentUnit.Пример:

final Waiter waiter = new Waiter(); 

    new Thread(() -> { 
    doSomeWork(); 
    waiter.assertTrue(true); 
    waiter.resume(); 
    }).start(); 

    // Wait for resume() to be called 
    waiter.await(1000); 
Смежные вопросы