обнаружение состояния гонки с помощью findbugs или другого инструмента анализа

Ниже bean-компонент не является потокобезопасным: метод addIfNotExist не синхронизирован, поэтому возможно, что один и тот же термин будет добавлен дважды из-за состояния гонки. Я аннотировал класс, используя аннотацию JCIP @ThreadSafe, надеясь, что FindBugs обнаружит, что реализация не является потокобезопасной, и пометит это как ошибку, но это не так. Существуют ли какие-либо инструменты, которые идентифицируют этот тип ошибок в кодовой базе?

Методы addIfNotExist и isExist должны быть синхронизированы, чтобы сделать этот компонент потокобезопасным. Следует ли также синхронизировать метод isExist?

package com.test;

import java.util.ArrayList;

import java.util.Collection;

import net.jcip.annotations.GuardedBy;

import net.jcip.annotations.ThreadSafe;

@ThreadSafe

public class Dictionary {

    @GuardedBy("this")
    public Collection<String> terms = new ArrayList<String>();

    public void addIfNotExist(final String input) {
        if (!this.terms.contains(input)) {
            this.terms.add(input);
        }
    }

    public boolean isExist(final String input){
        return this.terms.contains(input);
    }

    public void remove(final String input){
        this.terms.remove(input);
    }
}

person dsatish    schedule 06.04.2011    source источник


Ответы (3)


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

FindBugs (или вообще любые инструменты статического анализа) могут зайти так далеко в обнаружении непоточнобезопасного кода: по самому своему определению условия гонки чувствительны ко времени — для их проявления требуется много запусков выполнения, поэтому статический анализ терпит неудачу в этом отношении, потому что они не запускают никакого кода и ищут только общие сигнатуры кода. ИМХО, лучшие вещи для обнаружения проблем:

  • Вторая пара глаз — тщательная проверка кода коллегами, знакомыми с кодом, — помогает найти ошибки, которые не сразу очевидны для первоначального автора.

  • Непрерывная интеграция и всесторонние автоматические тесты, которые реализуют многопоточность на различном оборудовании и безжалостно исследуют любые «периодические» сбои тестов.

Отвечая на второй вопрос, да, все методы, которые ссылаются на terms, должны быть защищены монитором синхронизации, независимо от того, является ли это операцией записи или чтения; подумайте, что произойдет, если поток A вызовет remove("BOB"), в то время как поток B вызовет isExists("BOB"), когда у вас нет синхронизации - поток A будет сжимать список массивов, в то время как поток B будет пытаться пройти по нему.

В лучшем случае вы не сможете определить результат isExists("BOB"), но вполне возможно, что B может периодически генерировать исключение IndexOutOfBounds, поскольку размер массива мог измениться (т.е. уменьшиться) по мере его обхода.

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

person rhu    schedule 06.04.2011
comment
Спасибо за ваш ответ - ваши рассуждения о том, почему isExists должны быть синхронизированы, имеют смысл. - person dsatish; 07.04.2011

Это то, что вы можете использовать во время выполнения (во время автоматических модульных тестов или интеграционных тестов или чего-то еще), чтобы помочь найти проблемы с потоками: IBM ConTest (тестирование параллелизма)

Описание ConTest: «Технология ConTest является инновационной и нелогичной. В частности, ConTest систематически и прозрачно планирует выполнение программных потоков таким образом, чтобы программные сценарии, которые могут содержать условия гонки, взаимоблокировки и другие периодически возникающие ошибки — все вместе называемые синхронизацией проблемы - вынуждены появляться с высокой частотой. При этом ConTest резко повышает качество тестирования и снижает затраты на разработку, так как ошибки обнаруживаются раньше в процессе тестирования."

person Matt Crinklaw-Vogt    schedule 06.04.2011
comment
Где можно скачать ConTest? Все, что я нашел, это мертвые ссылки и пустые результаты поиска. - person Noel Yap; 13.12.2011

Чтобы найти такие неправильно синхронизированные блоки кода, я использую следующий алгоритм:

Запишите потоки для всех полевых модификаций с использованием инструментов. Если поле изменяется более чем одним потоком без синхронизации, я обнаружил гонку данных.

Я реализовал этот алгоритм внутри http://vmlens.com, который является динамическим инструментом для поиска гонок данных внутри java-программ.

person Thomas Krieger    schedule 24.09.2013