Автор оригинала: Pankaj Kumar.
Взаимоблокировка в java-это ситуация программирования, при которой два или более потоков блокируются навсегда. Ситуация взаимоблокировки Java возникает, по крайней мере, с двумя потоками и двумя или более ресурсами. Здесь я написал простую программу, которая вызовет сценарий взаимоблокировки java, а затем мы посмотрим, как его проанализировать.
Тупик в Java
Давайте взглянем на простую программу, в которой я создам тупик в потоках java.
package com.journaldev.threads; public class ThreadDeadlock { public static void main(String[] args) throws InterruptedException { Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1"); Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2"); Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3"); t1.start(); Thread.sleep(5000); t2.start(); Thread.sleep(5000); t3.start(); } } class SyncThread implements Runnable{ private Object obj1; private Object obj2; public SyncThread(Object o1, Object o2){ this.obj1=o1; this.obj2=o2; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " acquiring lock on "+obj1); synchronized (obj1) { System.out.println(name + " acquired lock on "+obj1); work(); System.out.println(name + " acquiring lock on "+obj2); synchronized (obj2) { System.out.println(name + " acquired lock on "+obj2); work(); } System.out.println(name + " released lock on "+obj2); } System.out.println(name + " released lock on "+obj1); System.out.println(name + " finished execution."); } private void work() { try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } } }
В приведенной выше программе SyncThread реализует управляемый интерфейс и работает с двумя объектами, получая блокировку на каждом из них по одному с помощью синхронизированного блока.
В основном методе у меня есть три потока, запущенных для SyncThread, и между каждым из потоков есть общий ресурс. Потоки выполняются таким образом, что он сможет получить блокировку первого объекта, но когда он пытается получить блокировку второго объекта, он переходит в состояние ожидания, потому что он уже заблокирован другим потоком. Это формирует циклическую зависимость для ресурса между потоками, вызывающую взаимоблокировку.
Когда я выполняю вышеуказанную программу, вот сгенерированный вывод, но программа никогда не завершается из-за тупика в потоках java.
t1 acquiring lock on java.lang.Object@6d9dd520 t1 acquired lock on java.lang.Object@6d9dd520 t2 acquiring lock on java.lang.Object@22aed3a5 t2 acquired lock on java.lang.Object@22aed3a5 t3 acquiring lock on java.lang.Object@218c2661 t3 acquired lock on java.lang.Object@218c2661 t1 acquiring lock on java.lang.Object@22aed3a5 t2 acquiring lock on java.lang.Object@218c2661 t3 acquiring lock on java.lang.Object@6d9dd520
Здесь мы можем четко определить ситуацию тупика по выходным данным, но в реальных приложениях очень трудно найти ситуацию тупика и отладить их.
Как обнаружить взаимоблокировку в Java
Чтобы обнаружить взаимоблокировку в java, нам нужно посмотреть дамп потока java приложения, в последнем посте я объяснил, как мы можем создать дамп потока с помощью профилировщика VisualVM или с помощью утилиты jstack
.
Вот дамп потока вышеприведенной программы.
2012-12-27 19:08:34 Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode): "Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f658> (a java.lang.Object) - locked <0x000000013df2f678> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f678> (a java.lang.Object) - locked <0x000000013df2f668> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000] java.lang.Thread.State: BLOCKED (on object monitor) at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f668> (a java.lang.Object) - locked <0x000000013df2f658> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177) "Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:503) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) - locked <0x000000013de75320> (a java.lang.ref.Reference$Lock) "VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable "GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable "GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable "GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable "GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable "GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable "GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable "GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable "GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable "VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition JNI global references: 114 Found one Java-level deadlock: ============================= "t3": waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object), which is held by "t1" "t1": waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object), which is held by "t2" "t2": waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object), which is held by "t3" Java stack information for the threads listed above: =================================================== "t3": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f658> (a java.lang.Object) - locked <0x000000013df2f678> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "t1": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f668> (a java.lang.Object) - locked <0x000000013df2f658> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) "t2": at com.journaldev.threads.SyncThread.run(ThreadDeadlock.java:41) - waiting to lock <0x000000013df2f678> (a java.lang.Object) - locked <0x000000013df2f668> (a java.lang.Object) at java.lang.Thread.run(Thread.java:722) Found 1 deadlock.
Вывод дампа потока четко показывает ситуацию взаимоблокировки, а также вовлеченные потоки и ресурсы, вызывающие ситуацию взаимоблокировки.
Для анализа взаимоблокировки нам нужно искать потоки с состоянием ЗАБЛОКИРОВАНО , а затем ресурсы, которые он ожидает блокировки . Каждый ресурс имеет уникальный идентификатор, с помощью которого мы можем определить, какой поток уже заблокировал объект. Например, поток “t3” ожидает блокировки 0x000000013df2f658, но он уже заблокирован потоком “t1”.
Как только мы проанализируем ситуацию взаимоблокировки и выясним, какие потоки вызывают взаимоблокировку, нам необходимо внести изменения в код, чтобы избежать ситуации взаимоблокировки.
Как избежать взаимоблокировки в java
Вот некоторые из рекомендаций, с помощью которых мы можем избежать большинства тупиковых ситуаций.
Избегайте вложенных блокировок : Это наиболее распространенная причина взаимоблокировок, избегайте блокировки другого ресурса, если он у вас уже есть. Практически невозможно получить тупиковую ситуацию, если вы работаете только с одной блокировкой объекта. Например, вот еще одна реализация метода run() без вложенной блокировки, и программа успешно запускается без тупиковой ситуации.
- Заблокируйте только то, что требуется : Вы должны получить блокировку только на ресурсах, с которыми вам нужно работать, например, в приведенной выше программе я блокирую полный ресурс объекта, но если нас интересует только одно из его полей, то мы должны заблокировать только это конкретное поле, а не полный объект.
- Избегайте бесконечного ожидания : вы можете попасть в тупик, если два потока ждут друг друга до бесконечности, используя соединение потоков . Если вашему потоку приходится ждать завершения другого потока, всегда лучше использовать соединение с максимальным временем, которое вы хотите потратить на ожидание завершения потока.
Это все для взаимоблокировки в потоках java.