Статьи

Резьба

обзор

Thread jiggler — это простая инфраструктура тестирования кода для выявления проблем с многопоточностью. Он работает путем изменения байт-кода классов во время выполнения, чтобы вставить вызовы Thread.yield () между инструкциями — «перемешивая» потоки. Это значительно увеличивает вероятность обнаружения проблем с многопоточностью и делает это без необходимости изменения рабочего кода.

Фон

Недавно я исследовал, как тестировать многопоточный код на наличие потоков, и узнал об инструменте от IBM под названием ConTest , но не смог найти код, который мог бы использовать сам. Естественно, я думал, что у меня появится шип.

Рассмотрим этот канонический простой, но небезопасный для потока класс:

1
2
3
4
5
private int count = 0 ;
 
    public void count() {
        count++;
    }

Байт-код метода подсчета:

1
2
3
4
5
DUP
GETFIELD asm/Foo.counter : I
ICONST_1
IADD
PUTFIELD asm/Foo.counter : I

Это обеспечивает несколько мест, где может быть переключение контекста, что означает, что счетчик может быть увеличен, но не сохранен, как ожидалось. Давайте рассмотрим быстрый юнит-тест:

01
02
03
04
05
06
07
08
09
10
11
12
13
Counter counter = new BadCounter();
    int n = 1000;
 
    @Test
    public void singleThreadedTest() throws Exception {
 
        for (int i = 0; i < n; i++) {
            counter.count();
        }
 
        assertEquals(n, counter.getCount());
    }
    ...

Этот тест выполняется в одном потоке и проходит. Давайте попробуем запустить это в двух потоках и посмотрим, не получится ли это.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public void threadedTest() throws Exception {
 
        final CompletionService<Void> service = new ExecutorCompletionService<Void>(Executors.newFixedThreadPool(2));
 
        for (int i = 0; i < n; i++) {
            service.submit(new Callable<Void>() {
                @Override
                public Void call() {
                    counter.count();
                    return null;
                }
            });
        }
 
        for (int i = 0; i < n; ++i) {
            service.take().get();
        }
 
        assertEquals(n, counter.getCount());
     }

Это также проходит. На моем компьютере я могу увеличить n до 100 000, прежде чем он начнет последовательно отказывать.

1
2
Expected :1000000
Actual   :999661

Только 0,04% тестов имели проблемы. Что мы узнали? Мы изучили простой способ запуска многопоточного теста, но мы узнали это, потому что мы не можем контролировать, когда потоки выполняют свою работу, это немного проб и ошибок.

Резьба

Поэтому одна из проблем, возникающих при использовании кода для поиска дефектов потоков, заключается в том, что вы не можете контролировать, когда потоки будут давать результаты. Тем не менее, мы можем переписать байт-код, чтобы вставить Thread.yield () в байт-код между инструкциями. В приведенном выше примере мы можем получить код для создания дополнительных проблем, изменив байт-код:

1
2
3
4
5
6
DUP
GETFIELD asm/Foo.counter : I
INVOKESTATIC java/lang/Thread.yield ()V
ICONST_1
IADD
PUTFIELD asm/Foo.counter : I

Используя ASM, мы можем создать переписчик для вставки этих вызовов. JigglingClassLoader переписывает классы на лету, добавляя эти вызовы. Из этого мы можем создать бегущий JUnit для запуска, используя новый загрузчик классов для теста.

1
2
3
4
@Jiggle("threadjiggler.test.*")
public class BadCounterTest {
    ...
}

Сейчас выполняется тест:

1
2
Expected :1000000
Actual   :836403

Количество тестов, в которых мы видим проблему с многопоточностью, увеличилось до 16%. Мы сделали это без перекомпиляции кода или влияния на другие модульные тесты, работающие в той же JVM.

Упражнение для читателя

SimpleDateFormat — это хорошо известный, не потокобезопасный класс в Java. Напишите тест, который покачивает класс. Почему это не потокобезопасно? Как бы вы переписали его так, чтобы он был потокобезопасным? Как вы можете сделать это без использования ThreadLocal, блокировки или синхронизации?

Исходный код

Код для этого можно найти на Github .

Дальнейшее чтение

Я написал пост о тестировании многопоточного кода на корректность . Вы также можете прочитать более широко:

Ссылка: Тема Jiggling от нашего партнера JCG Алекса Коллинза в блоге Алекса Коллинза .