Возможно ли во время выполнения программно проверить имя потока, удерживающего блокировку данного объекта?
Программно определить, какой поток Java удерживает блокировку
Ответы (9)
Вы можете только определить, удерживает ли текущий поток обычную блокировку (Thread.holdsLock(Object)
). Вы не можете получить ссылку на поток с блокировкой без собственного кода.
Однако, если вы делаете что-то сложное с многопоточностью, вы, вероятно, захотите ознакомиться с пакетами java.util.concurrent. ReentrantLock
позволяет вам чтобы получить его владельца (но это защищенный метод, поэтому вам придется его расширить). В зависимости от вашего приложения вполне может случиться так, что, используя пакеты параллелизма, вы обнаружите, что вам не нужно в конце концов получать владельца блокировки.
Существуют непрограммные методы поиска владельцев блокировок, такие как сигнализация JVM о выдаче дампа потока в stderr, которые полезны для определения причины взаимоблокировок.
Вы можете добраться до замков, удерживаемых нитями, с помощью отражения. Это работает только с java 1.6.
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] ti = bean.getThreadInfo(bean.getAllThreadIds(), true, true);
В каждом из этих объектов ThreadInfo есть объекты LockInfo, для которых вы можете использовать identityHashCode для сравнения с рассматриваемой блокировкой.
Начиная с версии 1.6, вы можете использовать JMX для выполнения всевозможных интересных задач, включая поиск удерживаемых блокировок. Вы не можете получить фактический объект, но вы получаете хеш-значение класса и идентификатора (которое не является уникальным).
В одном из моих блогов есть пример.
Запустите jconsole. Он включен в Java SDK и запускается из командной строки. Я не уверен, какую ОС вы используете, но в Windows вы можете просто передать ей PID процесса Java. Это должно помочь вам найти поток, который вызывает проблему. Или вы можете использовать коммерческий профилировщик, такой как YourKit, или любое количество других профилировщиков.
В версии 1.5 вы можете найти все потоки и получить состояние каждого из них, например, так:
Map<Thread,StackTraceElement[]> map = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> threadEntry : map.entrySet()) {
log.info("Thread:"+threadEntry.getKey().getName()+":"+threadEntry.getKey().getState());
for (StackTraceElement element : threadEntry.getValue()) {
log.info("--> "+element);
}
}
Thread.getState предоставляет информацию о том, заблокирован ли поток, ОЖИДАЕТ ли его и т. д., см. jdk API ThreadState
если это блокировка повторного входа, вы можете проверить, удерживается ли она текущим потоком
final ReentrantLock lock = new ReentrantLock();
lock.isHeldByCurrentThread();
Вы можете проверить блокировку конкретного объекта, вызвав метод wait()
или notify()
для этого объекта. Если объект не удерживает блокировку, он выдаст llegalMonitorStateException
.
2- Вызовом метода holdsLock(Object o)
. Это вернет логическое значение.
Вы можете использовать переменную для хранения текущего потока, когда вы берете блокировку, а затем распечатать ее, если кто-то другой пытается ее использовать.
Thread holderOfLock = null;
Object theLock = new Object();
public void doStuff()
{
if(holderOfLock != null)
{
//get and print name of holderOfLock-thread or get stacktrace etc.
}
synchronized (theLock)
{
holderOfLock = Thread.currentThread();
//do stuff...
holderOfLock = null;
}
}
Некрасиво, но работает.
String findLockOwner(ReentrantLock lock) {
String patternStr = "\\[Locked by thread (\\S+)\\]";
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(lock.toString());
boolean matchFound = matcher.find();
if (matchFound && matcher.groupCount() >= 1) {
return matcher.group(1);
}
}