2012-06-19 2 views
3

У меня возникла странная проблема в одном из наших проектов. Мы используем JUnit для запуска наших модульных тестов, и некоторое время назад мы начали параллельно запускать пробные тесты, чтобы ускорить выполнение. В большинстве случаев все в порядке, но время от времени почти все наши тесты терпят неудачу. В следующем прогоне все они проходят снова без изменения кода.многопотоковые и статические блоки

Ошибки, по-видимому, указывают на то, что некоторые статические экземпляры не инициализируются правильно или не используются до завершения инициализации в многопоточном случае. (Я не могу отладить это, так как проблема никогда не появлялась при отладке ->Heisenbug.)

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

Конкретный вопрос: при объявлении переменной, как показано ниже, существует ли вероятность того, что инициализация a или b не завершилась, когда foo() или bar() вызываются другим потоком? Я думал, что статический блок будет гарантированно выполняться до того, как любой из методов будет вызван. Или могут возникнуть проблемы с загрузкой классов? Или известные ошибки в JRE (мы в настоящее время застряли на 1.6.0_21, новые версии еще не предоставлены нашим ИТ-отделом)?

class C { 
    private static final A a; 
    private static final B b; 

    static { 
     a = new A(...); 
     b = new B(...); 
    } 

    public static void foo() { 
     useA(); 
    } 

    public static void bar() { 
     useB(); 
    } 

} 

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

Спасибо,

Axel

+0

«new A()' или 'new B()' запускать новые потоки? Вы пытались изолировать проблему и создать [SSCCE] (http://sscce.org), который воспроизводит проблему? – assylias

+0

Как я знаю, это плохая практика использования целого класса со статическими полями и методами, особенно при многопоточности. Почему бы вам не создать нестатический класс и сохранить его в статическом поле? – alaster

+0

Вот что делается здесь. a и b являются экземплярами классов, которые обеспечивают заранее рассчитанные значения. Оба класса неизменяемы и имеют методы, которые возвращают эти предварительно рассчитанные значения. Вычисления выполняются в конструкторах соответствующих классов. При доступе имеется не более чем проверка аргументов, вычисление индекса и возврат элемента массива, содержащего ранее вычисленное значение. – Axel

ответ

0

Это, скорее всего, из-за проблем параллелизма, особенно, если вы звоните static вещи. попробуйте синхронизировать свои темы, например:

class C { 
    private static final A a; 
    private static final B b; 

    static { 
     a = new A(...); 
     b = new B(...); 
    } 

    public synchronized static void foo() { 
     useA(); 
    } 

    public synchronized static void bar() { 
     useB(); 
    } 

} 
+0

классы A и B являются неизменяемыми и тщательно протестированы потокобезопасными. Здесь не нужно использовать синхронизацию. foo() и bar() называются миллионы раз во время наших тестов и либо всегда терпят неудачу, либо никогда не сбой, поэтому я думаю, что это должно быть связано с инициализацией. – Axel

+0

Статические поля инициализируются ** один раз ** (они связаны с классом и не связаны с экземпляром). Вы не указали ничего о том, как вызовы A и B вызывают из C, поэтому для меня они лишние в вашем примере. – m0skit0

+0

Да, инициализирован один раз. И кажется, что во время их инициализации что-то идет не так. – Axel

0

Там могут быть проблемы, если A или B создать поток или иным образом выполнить код в другом потоке. Вы все равно хотите, чтобы статика была неизменной.

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

Смежные вопросы