Java 多线程死锁及示例2025年4月8日 | 阅读 8 分钟 Java 中的死锁是多线程的一部分。当一个线程等待另一个线程获得的某个对象的锁,而第二个线程又等待第一个线程获得的某个对象的锁时,就可能发生死锁。由于两个线程都在等待对方释放锁,这种情况就称为死锁。 ![]() 死锁的条件
需要注意的是,即使上述条件中的任何一个不满足,也不会发生死锁。 Java 死锁示例示例编译并运行输出 Thread 1: locked resource 1 Thread 2: locked resource 2 说明 有两个资源:resource1 和 resource2。第一个线程 t1 锁定 resource1 并休眠。同样,第二个线程 t2 锁定 resource2 并休眠。每个线程休眠 100 毫秒。 之后,第一个线程 t1 尝试获取 resource2,第二个线程 t2 尝试获取 resource1。然而,resource1 已被线程 t1 获取,resource2 已被线程 t2 获取,导致死锁情况,输出也证实了这一点。第二和第四个打印语句没有执行。 更复杂的死锁死锁可能还包括两个以上的线程。原因是检测死锁可能很困难。下面是一个四个线程发生死锁的示例 线程 1 锁定 A,等待 B 线程 2 锁定 B,等待 C 线程 3 锁定 C,等待 D 线程 4 锁定 D,等待 A 线程 1 等待线程 2,线程 2 等待线程 3,线程 3 等待线程 4,线程 4 等待线程 1。 死锁检测观察以下步骤来检测死锁。 步骤 1:分别使用命令 javac 和 Java 编译并执行程序。 这里,我们执行上面编写的包含死锁的程序。 ![]() 步骤 2:打开另一个命令提示符窗口,运行命令 jps -l 命令 jps -l 列出所有正在运行的 Java 进程(带进程 ID)。 ![]() 我们可以看到 Main 的进程 ID 是 8680。 步骤 3:在命令提示符中键入以下命令并运行 将 <PID> 替换为进程 ID 8680 并观察输出。 ![]() 输出 8680: 2025-04-03 13:46:30 Full thread dump Java HotSpot(TM) 64-Bit Server VM (12.0.2+10 mixed mode, sharing): Threads class SMR info: _java_thread_list=0x000000ddaf977c70, length=12, elements={ 0x000000ddae6d0000, 0x000000ddae6d1000, 0x000000ddae6e9800, 0x000000ddae6f0800, 0x000000ddae6f1800, 0x000000ddae6f5800, 0x000000ddae6f8800, 0x000000ddaf911000, 0x000000ddaf95b000, 0x000000ddaf976000, 0x000000ddaf976800, 0x000000dd8f70f800 } "Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6d0000 nid=0x5e68 waiting on condition [0x000000ddaf14f000] java.lang.Thread.State: RUNNABLE at java.lang.ref.Reference.waitForReferencePendingList(java.base@12.0.2/Native Method) at java.lang.ref.Reference.processPendingReferences(java.base@12.0.2/Reference.java:241) at java.lang.ref.Reference$ReferenceHandler.run(java.base@12.0.2/Reference.java:213) "Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6d1000 nid=0x29ac in Object.wait() [0x000000ddaf24e000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(java.base@12.0.2/Native Method) - waiting on <0x000000008a90af78> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:155) - locked <0x000000008a90af78> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:176) at java.lang.ref.Finalizer$FinalizerThread.run(java.base@12.0.2/Finalizer.java:170) "Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6e9800 nid=0x5220 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Attach Listener" #5 daemon prio=5 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f0800 nid=0x52d8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #6 daemon prio=9 os_prio=2 cpu=15.63ms elapsed=448.77s tid=0x000000ddae6f1800 nid=0x1c4 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE No compile task "C1 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f5800 nid=0x5338 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE No compile task "Sweeper thread" #9 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=448.77s tid=0x000000ddae6f8800 nid=0x5e70 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Service Thread" #10 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=448.76s tid=0x000000ddaf911000 nid=0x3040 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Common-Cleaner" #11 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf95b000 nid=0x888 in Object.wait() [0x000000ddaff2e000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(java.base@12.0.2/Native Method) - waiting on <0x000000008a9e7440> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:155) - locked <0x000000008a9e7440> (a java.lang.ref.ReferenceQueue$Lock) at jdk.internal.ref.CleanerImpl.run(java.base@12.0.2/CleanerImpl.java:148) at java.lang.Thread.run(java.base@12.0.2/Thread.java:835) at jdk.internal.misc.InnocuousThread.run(java.base@12.0.2/InnocuousThread.java:134) "Thread-0" #12 prio=5 os_prio=0 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf976000 nid=0x2100 waiting for monitor entry [0x000000ddb002f000] java.lang.Thread.State: BLOCKED (on object monitor) at Main$1.run(Main.java:15) - waiting to lock <0x000000008a800800> (a java.lang.String) - locked <0x000000008a800000> (a java.lang.String) "Thread-1" #13 prio=5 os_prio=0 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf976800 nid=0x593c waiting for monitor entry [0x000000ddb012f000] java.lang.Thread.State: BLOCKED (on object monitor) at Main$2.run(Main.java:30) - waiting to lock <0x000000008a800000> (a java.lang.String) - locked <0x000000008a800800> (a java.lang.String) "DestroyJavaVM" #14 prio=5 os_prio=0 cpu=31.25ms elapsed=448.75s tid=0x000000dd8f70f800 nid=0x5db4 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "VM Thread" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000ddae6c9000 nid=0x5da4 runnable "GC Thread#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f750000 nid=0x5b84 runnable "G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f75d800 nid=0x1134 runnable "G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f760000 nid=0x3624 runnable "G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f7fa800 nid=0x56b8 runnable "G1 Young RemSet Sampling" os_prio=2 cpu=0.00ms elapsed=448.78s tid=0x000000dd8f7fb800 nid=0x2464 runnable "VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=448.75s tid=0x000000ddaf959800 nid=0x19bc waiting on condition JNI global refs: 4, weak refs: 0 Found one Java-level deadlock: ============================= "Thread-0": waiting to lock monitor 0x000000ddae6db280 (object 0x000000008a800800, a java.lang.String), which is held by "Thread-1" "Thread-1": waiting to lock monitor 0x000000ddae6d9280 (object 0x000000008a800000, a java.lang.String), which is held by "Thread-0" Java stack information for the threads listed above: =================================================== "Thread-0": at Main$1.run(Main.java:15) - waiting to lock <0x000000008a800800> (a java.lang.String) - locked <0x000000008a800000> (a java.lang.String) "Thread-1": at Main$2.run(Main.java:30) - waiting to lock <0x000000008a800000> (a java.lang.String) - locked <0x000000008a800800> (a java.lang.String) Found 1 deadlock. 我们可以看到输出显示找到 1 个死锁。 防止死锁问题的解决方案在于其根源。在死锁中,访问资源 A 和 B 的模式是主要问题。要解决该问题,我们只需重新排序访问共享资源的语句即可。 示例编译并运行输出 In block 1 In block 2 说明 在上面的程序中,有两个线程 t1 和 t2。在生成两个线程后,线程 t1 获取资源 b 并休眠 100 毫秒。在此期间,线程 t2 也尝试获取资源 b,由于它已被线程 t1 获取,因此线程 t2 等待。 之后,线程 t1 从休眠中唤醒,对资源 a 应用锁定,然后完成其执行。从而释放资源 a 和 b。线程 t2 现在获取资源 b,然后获取资源 a 并执行其任务。请注意,两个线程都完成了它们的工作。因此,没有发生死锁。 如何在 Java 中避免死锁?死锁无法完全解决。但是,我们可以通过遵循以下基本规则来避免它们:
要阅读更多点击这里 要记住的重要事项
Java 死锁选择题1. Java 中的死锁是什么?
答案 4 解释:当一个线程等待另一个线程获得的某个对象的锁,而第二个线程又等待第一个线程获得的某个对象的锁时,就可能发生死锁。由于两个线程都在等待对方释放锁,这种情况就称为死锁。 2. 下列哪项不是发生死锁的条件?
答案 3 解释:抢占不是死锁的条件,因为资源不能被强制从持有它们的进程中移除。进程必须自愿释放资源。 3. 防止死锁的最佳方法是什么?
答案 2 解释:死锁无法自动解决。开发人员需要避免导致死锁的情况,或者使用锁层次结构等技术来确保线程按特定顺序获取锁。 4. 避免 Java 中死锁的常用方法是什么?
答案 1 解释:使用带超时的锁以避免无限等待。 5. Java 开发工具包 (JDK) 中的哪个工具可以帮助诊断死锁?
答案 2 解释:用于诊断死锁的主要 JDK 工具是 jstack,它可以用来生成线程转储,显示所有线程的状态,包括参与死锁的线程,并确定它们正在等待哪些锁。 下一主题Java 线程间通信 |
我们请求您订阅我们的新闻通讯以获取最新更新。