Статьи

Устранение неполадок и устранение тупиков в Java

Одной из замечательных особенностей ежегодных конференций JavaOne является презентация нескольких технических лабораторий и лабораторий по устранению неполадок, представленных экспертами в данной области. Одна из этих лабораторий привлекла мое внимание в этом году: « HOL6500 — Поиск и устранение тупиков Java », представленный чемпионом Java Хайнцем Кабуцем . Это одна из лучших презентаций на эту тему. Я рекомендую вам загрузить, запустить и изучить лаборатории самостоятельно.

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

Java тупик: что это?

Истинный тупик Java по сути можно описать как ситуацию, когда два или более потоков заблокированы навсегда, ожидая друг друга. Эта ситуация сильно отличается от других более распространенных «повседневных» моделей проблем потоков, таких как состязание блокировок и гонки потоков, потоки, ожидающие блокирования вызовов ввода-вывода и т. Д. Такая ситуация блокировки блокировки порядка блокировки может быть визуализирована, как показано ниже:

В приведенном выше визуальном примере попытка потоков A и Thread B получить 2 блокировки в разных порядках фатальна. Как только потоки достигли заблокированного состояния, они никогда не смогут восстановиться, что заставит вас перезапустить уязвимый процесс JVM.

Хайнц также описывает другой тип тупика: тупик ресурса . На сегодняшний день это наиболее распространенный шаблон проблем с потоками, который я видел в своем опыте устранения неполадок в корпоративной системе Java EE. Блокировка ресурса — это, по сути, сценарий, когда один или несколько потоков ожидают получения ресурса, который никогда не будет доступен, например, истощение пула JDBC.

Блокировка блокировки тупиков

К настоящему времени вы должны знать, что я большой поклонник анализа дампа потоков JVM ; критически важный навык для людей, вовлеченных в разработку Java / Java EE или поддержку производства. Хорошая новость заключается в том, что взаимоблокировки на уровне Java можно легко определить «из коробки» большинством форматов дампов потоков JVM (HotSpot, IBM VM…), поскольку они содержат собственный механизм обнаружения взаимоблокировок, который фактически покажет вам потоки, участвующие в настоящий сценарий взаимоблокировки на уровне Java вместе с трассировкой стека выполнения. Дамп потока JVM может быть захвачен с помощью выбранного вами инструмента, такого как JVisualVM, jstack или изначально, например kill -3 <PID> в ОС на основе Unix. Найдите ниже раздел Обнаружение взаимоблокировки уровня Java JVM после запуска лабораторной работы 1:

Теперь это самая простая часть … Суть анализа первопричин заключается в том, чтобы понять, почему такие потоки в первую очередь вовлечены в тупиковую ситуацию. Блокировки упорядочивания блокировок могут быть вызваны из кода вашего приложения, но, если вы не участвуете в программировании с высоким параллелизмом, есть вероятность, что код виновника — это API или инфраструктура третьей части, которые вы используете, или сам контейнер Java EE, когда это применимо.

Теперь давайте рассмотрим ниже стратегии разрешения взаимных блокировок, представленные Heinz:

# Разрешение тупиковых ситуаций с помощью глобального порядка (см. Решение lab1)

  • По сути, включает в себя определение глобального порядка для блокировок, который всегда предотвращал бы тупик (см. Решение lab1)

# Разрешение тупиковых ситуаций от TryLock (см. Решение lab2)

  • Блокировка первого замка
  • Затем попробуйте заблокировать второй замок
  • Если вы можете заблокировать его, вы можете идти
  • Если вы не можете, подождите и попробуйте снова

Вышеприведенная стратегия может быть реализована с использованием Java Lock & ReantrantLock, которая также дает вам гибкость в настройке тайм-аута ожидания, чтобы предотвратить голодание потока в случае, если первая блокировка получена слишком долго.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public interface Lock {
 
void lock();
 
void lockInterruptibly() throws InterruptedException;
 
boolean tryLock();
 
boolean tryLock(long timeout, TimeUnit unit)
 
throws InterruptedException;
 
void unlock();
 
Condition newCondition();
 
}

Если вы посмотрите на реализацию JBoss AS7, вы заметите, что Lock & ReantrantLock широко используются на основных уровнях реализации, таких как:

  • Служба развертывания
  • Реализация EJB3 (широко используется)
  • Кластеризация и управление сессиями
  • Внутренний кеш и структуры данных (LRU, ConcurrentReferenceHashMap…)

Теперь и, по мнению Хайнца, стратегия разрешения тупиковых ситуаций № 2 может быть достаточно эффективной, но также требуется надлежащая осторожность, например, снятие всех удерживаемых блокировок с помощью блока finally {}, в противном случае вы можете превратить свой сценарий тупиковой ситуации в живую блокировку.

Ресурсные тупики

Теперь давайте перейдем к сценариям тупика ресурсов. Я рад, что лаборатория № 3 Хайнца рассмотрела это, поскольку, по моему опыту, это наиболее распространенный сценарий «тупика», который вы увидите, особенно если вы разрабатываете и поддерживает большие распределенные производственные системы Java EE.

Теперь давайте разберемся с фактами.

  • Блокировки ресурсов не являются настоящими блокировками уровня Java
  • JVM Thread Dump не будет магическим образом, если вы зашли в тупик этих типов. Это означает, что вам нужно больше анализировать и понимать эту проблему как отправную точку.
  • Анализ дампов потоков может быть особенно запутанным, когда вы только начинаете изучать, как читать дамп потоков, поскольку потоки часто отображаются как состояние RUNNING по сравнению с состоянием BLOCKED для взаимоблокировок уровня Java. На данный момент важно иметь в виду, что состояние потока не так важно для этого типа проблемы, например, состояние RUNNING! = Исправное состояние.
  • Подход анализа сильно отличается от взаимоблокировок на уровне Java. Вы должны сделать несколько снимков дампа потока и определить шаблоны проблем / ожиданий потока между каждым снимком. Вы сможете видеть, что потоки не движутся, например, потоки, ожидающие получения ресурса из пула, и другие потоки, которые уже получили такой ресурс и зависают …
  • Анализ дампа потока — не единственная точка данных / факт, важный здесь. Вам нужно будет собрать другие факты, такие как статистика по ресурсу (ресурсам), которого ожидают потоки, общее состояние промежуточного ПО или состояния среды и т. Д. Комбинация всех этих фактов позволит вам сделать вывод об основной причине вместе со стратегией разрешения, которая может или может не включать изменение кода.

Я вернусь к вам с большим количеством шаблонов проблем с дампами потоков, но сначала убедитесь, что вы знакомы с основными принципами дампов JVM в качестве отправной точки.

Вывод

Я надеюсь, что у вас была возможность просмотреть, запустить и насладиться лабораториями из презентации Хайнца так же, как и я. Параллельное программирование и устранение неполадок могут быть довольно сложными, но я все же рекомендую вам потратить некоторое время, пытаясь понять некоторые из этих принципов, поскольку я уверен, что в ближайшем будущем вы столкнетесь с ситуацией, которая заставит вас выполнить это глубокое погружение и приобрести те навыки и умения.

Ссылка: Устранение неполадок и устранение тупиковых ситуаций Java и их решение от нашего партнера JCG Пьера-Хьюга Шарбонно в блоге « Шаблоны поддержки Java EE и учебник по Java» .