死锁死锁的定义死锁的影响程序中的死锁例子例一

public class MustDeadlock extends Thread { int flag = 1; static Object o1 = new Object(); static Object o2 = new Object(); public static void main(String[] args) { MustDeadLock run1 = new MustDeadLock(); MustDeadLock run2 = new MustDeadLock(); run1.flag = 1; run2.flag = 0; run1.start(); run2.start(); } @Override public void run() { System.out.println("flag = " flag); if (flag == 1) { synchronized (o1) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) { System.out.println("线程1成功拿到两把锁!"); } } } if (flag == 0) { synchronized (o2) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) { System.out.println("线程2成功拿到两把锁!"); } } } } }

案例分析例二(转账操作)

public class TransferMoney implements Runnable { int flag = 1; static Account a = new Account(500); static Account b = new Account(500); public static void main(String[] args) throws InterruptedException { TransferMoney r1 = new TransferMoney(); TransferMoney r2 = new TransferMoney(); r1.flag = 1; r2.flag = 0; Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("a.balance = " a.balance); System.out.println("b.balance = " b.balance); } @Override public void run() { if (flag == 1) { transferMoney(a, b, 200); } if (flag == 0) { transferMoney(b, a, 200); } } private void transferMoney(Account from, Account to, int amount) { synchronized (from) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (to) { if (from.balance - to.balance < 0) { System.out.println("余额不足,转账失败!"); } from.balance -= to.balance; to.balance = from.balance; System.out.println("转账成功,转账共:" amount); } } } static class Account { int balance; public Account(int balance) { this.balance = balance; } } }

死锁产生的必要条件① 互斥条件

进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

② 请求与保持条件

进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

③ 不剥夺条件

进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

④ 循环等待条件

存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i 1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有,如例一所示。

如何定位死锁?jstack

这里以案例一为基础进行展示。

第一步:先运行列一;第二步:找到java在系统中的进程 id;方式一(直接通过任务管理器获取)
方式二(通过 Java 提供的程序获取)
  1. D:\development\jdk\1.8jdk\bin>jps 12160 Jps 45224 Launcher 23020 MustDeadLock
第三步:通过工具定位到死锁

还是在 Java JDK 的安装路径下的 bin 目录下打开 cmd 窗口,然后运行 jstack 进程id

D:\development\jdk\1.8jdk\bin>jstack 23020 2022-03-06 17:13:11 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode): "DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x00000204548cb000 nid=0x12220 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-1" #13 prio=5 os_prio=0 tid=0x00000204719b6000 nid=0xbd44 waiting for monitor entry [0x0000000c528ff000] java.lang.Thread.State: BLOCKED (on object monitor) at main.threaddemo.MustDeadLock.run(MustDeadLock.java:45) - waiting to lock <0x000000076c09a3a0> (a java.lang.Object) - locked <0x000000076c09a3b0> (a java.lang.Object) "Thread-0" #12 prio=5 os_prio=0 tid=0x00000204719b3000 nid=0xd260 waiting for monitor entry [0x0000000c527ff000] java.lang.Thread.State: BLOCKED (on object monitor) at main.threaddemo.MustDeadLock.run(MustDeadLock.java:33) - waiting to lock <0x000000076c09a3b0> (a java.lang.Object) - locked <0x000000076c09a3a0> (a java.lang.Object) "Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000002047198c800 nid=0x48f4 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x00000204718eb800 nid=0xa35c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x00000204718e6800 nid=0x9288 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x00000204718e4000 nid=0x7dcc waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x00000204718e1000 nid=0x101a4 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x00000204718de000 nid=0xb3b4 runnable [0x0000000c520fe000] java.lang.Thread.State: RUNNABLE at java.net.socketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x000000076bf8f8a0> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x000000076bf8f8a0> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000020471893000 nid=0x109e0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000020471892800 nid=0x6798 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000002046f4cb000 nid=0x5848 in Object.wait() [0x0000000c51dfe000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076be08ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x000000076be08ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000002046f4c4800 nid=0xee60 in Object.wait() [0x0000000c51cff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076be06c00> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000076be06c00> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x000002046f499000 nid=0x7284 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000204548e1800 nid=0xf530 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000204548e3000 nid=0x12aa0 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000204548e4000 nid=0x11d58 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000204548e5800 nid=0x67d8 runnable "GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00000204548e7800 nid=0xed20 runnable "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00000204548e8800 nid=0x2e10 runnable "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00000204548eb800 nid=0xd504 runnable "GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00000204548ec800 nid=0x10cc0 runnable "GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00000204548ed800 nid=0x8548 runnable "GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00000204548ee800 nid=0xac70 runnable "VM Periodic Task Thread" os_prio=2 tid=0x00000204719b1000 nid=0x4b64 waiting on condition JNI global references: 12 Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x000002046f4c8368 (object 0x000000076c09a3a0, a java.lang.Object), which is held by "Thread-0" "Thread-0": waiting to lock monitor 0x000002046f4caca8 (object 0x000000076c09a3b0, a java.lang.Object), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at main.threaddemo.MustDeadLock.run(MustDeadLock.java:45) - waiting to lock <0x000000076c09a3a0> (a java.lang.Object) - locked <0x000000076c09a3b0> (a java.lang.Object) "Thread-0": at main.threaddemo.MustDeadLock.run(MustDeadLock.java:33) - waiting to lock <0x000000076c09a3b0> (a java.lang.Object) - locked <0x000000076c09a3a0> (a java.lang.Object) Found 1 deadlock.

运行后主要观察

Java stack information for the threads listed above: =================================================== "Thread-1": at main.threaddemo.MustDeadLock.run(MustDeadLock.java:45) - waiting to lock <0x000000076c09a3a0> (a java.lang.Object) - locked <0x000000076c09a3b0> (a java.lang.Object) "Thread-0": at main.threaddemo.MustDeadLock.run(MustDeadLock.java:33) - waiting to lock <0x000000076c09a3b0> (a java.lang.Object) - locked <0x000000076c09a3a0> (a java.lang.Object) Found 1 deadlock.

通过 ThreadMXBean 工具类去检测死锁代码

public class ThreadMXBeanDetection implements Runnable { int flag = 1; static Object lock1 = new Object(); static Object lock2 = new Object(); public static void main(String[] args) throws InterruptedException { ThreadMXBeanDetection r1 = new ThreadMXBeanDetection(); ThreadMXBeanDetection r2 = new ThreadMXBeanDetection(); r1.flag = 1; r2.flag = 0; Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); Thread.sleep(3000); ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); if (deadlockedThreads != null && deadlockedThreads.length > 0) { for (int i = 0; i < deadlockedThreads.length; i ) { ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]); System.out.println("发现死锁" threadInfo.getThreadName()); } } } @Override public void run() { System.out.println("flag = " flag); if (flag == 1) { synchronized (lock1) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("线程1成功拿到两把锁!"); } } } if (flag == 0) { synchronized (lock2) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("线程2成功拿到两把锁!"); } } } } }

输出

flag = 1 flag = 0 发现死锁Thread-1 发现死锁Thread-0

常见的修复方式①避免策略②检测与恢复策略
实际开发中如何避免死锁
  • 设置超时时间;
  • 多使用并发类而不是自己设计锁;
  • 尽量降低锁的使用粒度:用不同的锁而不是一个锁;
  • 如果能使用同步代码块,就不使用同步方法:自已指定所对象;
  • 创建线程的时候命名尽量达到见名知义,方便后面排查问题;

小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏 评论转发 关注我,后面会有很多干货。

我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

java多线程与并发知识体系(多线程中的死锁概述)(1)

原文出处:www.shaoqun.com/a/1579342.html

,