Регистрация всех потоков, которые заблокировали мой объект

Мне нужно отследить все потоки, которые заблокировали один из моих объектов.

Можно ли подключиться к механизму неявной синхронизации в java и регистрировать идентификатор потока (или другую информацию) всякий раз, когда какой-либо поток получает блокировку?

Насколько мне известно, не существует метода, который я мог бы просто переопределить. В API отражения тоже ничего полезного не нашел. Я не нашел какого-либо внешнего инструмента, который мог бы отслеживать блокировки (VisualVM отслеживает только использование процессора и памяти, FindBugs использует статический анализ, javapathfinder, похоже, может тестировать только небольшие приложения, отличные от awt).

Изменить: кажется, что есть очень похожий вопрос Как в Java регистрировать сообщение каждый раз, когда монитор данного объекта входит или выходит из него?


person Prvaak    schedule 05.03.2014    source источник
comment
Мне нравится джстак. Он показывает все потоки и их статус ожидания.   -  person Ronald    schedule 05.03.2014
comment
К сожалению, jstack мне не поможет. Он предоставляет только снимок, а мне нужно отслеживать все изменения.   -  person Prvaak    schedule 05.03.2014
comment
правильно, но в случае проблем с блокировкой у вас будет положительная вероятность поймать некоторые ситуации WAIT. Многократное выполнение jstack даст вам представление о горячих точках и трассировках стека потоков, что даст вам представление о том, где искать в исходном коде. Потоки, удерживающие блокировки, как правило, более интересны, чем ожидающие, кстати.   -  person Ronald    schedule 05.03.2014


Ответы (4)


Казалось бы, ваше наиболее вероятное решение, которое будет работать правильно во всех случаях (за исключением требования, чтобы каждое использование вызывало пользовательскую функцию блокировки), - это инструментирование байт-кода. Вы найдете все случаи использования monitorenter и monitorexit и добавите свои собственные специальные инструкции для этих случаев. Здесь могут помочь два ресурса: http://www.correlsense.com/blog/java-bytecode-instrumentation-an-introduction/ (это запись в блоге с некоторой информацией) и http://commons.apache.org/proper/commons-bcel/manual.html (это библиотека, которая может вам помочь).

person user1676075    schedule 05.03.2014
comment
Спасибо. Ваш ответ подтолкнул меня в правильном направлении. Я хотел бы проголосовать за это, но у меня недостаточно репутации :( - person Prvaak; 07.03.2014

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

Однако вы можете поместить операторы журнала в синхронизированный блок, если использование таково, что ваша IDE может сообщить вам все места, где оно использовалось.

В противном случае вам, возможно, придется заменить объект монитора чем-то другим и иметь там свой код трассировки.

person Enno Shioji    schedule 05.03.2014

Если инструментарий кода приемлем, вы можете добавить оператор "log" в синхронизированный блок, который будет использовать Thread.currentThread() для получения текущего потока и IdentityHashMap для связывания синхронизированного объекта с коллекцией имен потоков. Конечно, потоки могут иметь одно и то же имя, и вам, возможно, придется фильтровать выполнение вызова журнала, чтобы не перегружаться ассоциациями объект->поток.

person Chris Gerken    schedule 05.03.2014

К сожалению, кажется, что это может быть достигнуто только с помощью манипуляций с байт-кодом. К счастью, манипуляции с байт-кодом не так сложны (благодаря ASM).

Я создал агент Java с пользовательским java.lang.instrument.ClassFileTransformer для изменения классов во время загрузки. Загрузчик класса перехватывает все синхронизированные блоки (они начинаются с инструкции MONITORENTER) и синхронизированные методы (они не начинаются с инструкции MONITORENTER) и вставляет в эти места вызов функции трассировки.

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

public static void trace(Object lock) {
    if (lock instanceof TraceMe) {
        // Do whatever you want here.
    }
}

Код агента можно найти в этом списке. Скомпилируйте его в файл jar и добавьте следующий файл MANIFEST.MF:

Manifest-Version: 1.0
Premain-Class: com.example.LockTracerAgent

Запустите его с помощью:

    java -Xbootclasspath/p:asm-4.2.jar:myapp.jar -javaagent:myagent.jar -jar myapp.jar
person Prvaak    schedule 07.03.2014