Java那些“锁”事 - 死锁及排查

死锁是两个或者两个以上的线程在执行过程中,因争夺资源而造成的一种互斥等待现象,若没有外界干涉那么它们将无法推进下去。如果系统资源不足,进程的资源请求都得到满足,死锁出现的可能性就很低,否则就会因为争夺有限的资源而陷入死锁。

死锁案例

 public static void main(String[] args) {

        final Object a = new Object();
        final Object b = new Object();

        new Thread(() -> {
            synchronized (a) {
                try {
                    System.out.println(Thread.currentThread().getName() + ",持有a锁希望获得b锁");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b) {
                    System.out.println(Thread.currentThread() + ",成功获得b锁");
                }
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (b) {
                try {
                     System.out.println(Thread.currentThread().getName() + ",持有b锁希望获得a锁");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a) {
                    System.out.println(Thread.currentThread() + ",成功获得a锁");
                }
            }
        }, "t2").start();
    }

        打印结果:

t1,持有a锁希望获得b锁
t2,持有b锁希望获得a锁

        可以看到案例中,t1持有a锁,希望获得b锁。而t2持有b锁,希望获得a锁。t1和t2就僵持在这里,程序得不到终止。

排除死锁

        通过jdk自带的工具、命令我们可以查看当前进程,以及进程中是否存在死锁情况。

jps -l

        通过jps -l 我们可以看到当前系统中正在运行的java进程:

$ jps -l
18064 com.tlh.comf._死锁及排查
3568
10644 org.jetbrains.jps.cmdline.Launcher
16676 sun.tools.jps.Jps
19544 org.jetbrains.idea.maven.server.RemoteMavenServer

        我们看到我们的死锁案例的进程id为:18064

jstack #{进程id}

        通过jstack #{进程id},我们可以查看当前进程中线程的情况和否存在死锁。比如,我们的死锁案例进程id为:18064。我们通过jstack 18064查看到的信息:

$ jstack 18064
2023-07-28 23:57:25
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b15 mixed mode):

"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000002ff3800 nid=0x50cc waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #13 prio=5 os_prio=0 tid=0x000000002980d800 nid=0x4c94 waiting for monitor entry [0x000000002a05e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.tlh.comf._死锁及排查.lambda$main$1(_死锁及排查.java:42)
        - waiting to lock <0x00000007161d8520> (a java.lang.Object)
        - locked <0x00000007161d8530> (a java.lang.Object)
        at com.tlh.comf._死锁及排查$$Lambda$2/824909230.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

"t1" #12 prio=5 os_prio=0 tid=0x000000002980b000 nid=0x4a84 waiting for monitor entry [0x0000000029f5f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.tlh.comf._死???及排查.lambda$main$0(_死锁及排查.java:28)
        - waiting to lock <0x00000007161d8530> (a java.lang.Object)
        - locked <0x00000007161d8520> (a java.lang.Object)
        at com.tlh.comf._死锁及排查$$Lambda$1/1534030866.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x0000000027c77000 nid=0x50a4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x0000000027b8a800 nid=0x1dc8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000027b84000 nid=0x2d4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000027b81000 nid=0xa8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000027b7e800 nid=0x39d8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000027b64000 nid=0x48fc runnable [0x000000002905e000]
   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:170)
        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 <0x00000007162cf3f0> (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 <0x00000007162cf3f0> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000027a11800 nid=0x5384 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000279be000 nid=0x4fc8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x00000000262d3800 nid=0x1204 in Object.wait() [0x0000000028cfe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000716008ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x0000000716008ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x00000000262cc800 nid=0x4de4 in Object.wait() [0x0000000028bff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000716006b50> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x0000000716006b50> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000000027982800 nid=0x5298 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000003009000 nid=0x4cbc runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000300a800 nid=0x3a54 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000300c000 nid=0x4098 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000300d800 nid=0x2e78 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000000300f800 nid=0x3f98 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000003012000 nid=0xd60 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000003015000 nid=0x3414 runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000003016000 nid=0x2204 runnable

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x0000000003017800 nid=0x4fbc runnable

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x0000000003018800 nid=0x49e8 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000027ced000 nid=0x4f44 waiting on condition

JNI global references: 335


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x00000000262d0678 (object 0x00000007161d8520, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x00000000262d2e58 (object 0x00000007161d8530, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at com.tlh.comf._死锁及排查.lambda$main$1(_死锁及排查.java:42)
        - waiting to lock <0x00000007161d8520> (a java.lang.Object)
        - locked <0x00000007161d8530> (a java.lang.Object)
        at com.tlh.comf._死锁及排查$$Lambda$2/824909230.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)
"t1":
        at com.tlh.comf._死锁及排查.lambda$main$0(_死锁及排查.java:28)
        - waiting to lock <0x00000007161d8530> (a java.lang.Object)
        - locked <0x00000007161d8520> (a java.lang.Object)
        at com.tlh.comf._死锁及排查$$Lambda$1/1534030866.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

        打印的信息中,最上部分我们可以看到t1和t2的状态均为:BLOCKED(阻塞)状态。打印信息的最下部分,我们可以看到:Found one Java-level deadlock,提示信息说,t2在等待 lock monitor 0x00000000262d0678,但是被t1持有。t1在等待lock monitor 0x00000000262d2e58,但是被t2持有。

<think>好的,我现在需要处理用户关于Java死锁排查方法和解决方案的查询。首先,我要回顾一下用户提供的系统级指令和之前的引用内容,确保回答符合要求。 用户明确要求用中文回答,并且要结构清晰,分步骤解决问题。同时,用户提到了几个引用,我需要参考这些引用中的信息,尤其是引用[1]、[2]、[3]、[4]、[5]中有关Java死锁的内容。 首先,死锁的定义:多个进程或线程因竞争资源而互相等待,无法继续执行。根据引用[1][2][3],这一点已经明确。接下来,排查方法部分,用户需要具体的步骤。根据引用[1]提到的,Java死锁排查可以使用jstack工具,而引用[2]和[3]也提到了类似的方法,比如线程转储和分析日志。同时,引用[4]提到了排序,引用[5]虽然主要讲数据库死锁,但部分思路可能适用,比如调整隔离级别和优化务逻辑,但这里需要专注于Java多线程的情况。 解决方案方面,引用[4]详细说明了排序,避免环路等待,而引用[2]和[3]也提到了预防策略,如顺序、超时机制等。需要将这些方法组织起来,分点说明。 另外,用户要求生成相关问题,我需要根据内容提出相关的问题,比如排查工具的其他方法、预防策略、与数据库死锁的区别等。 需要注意,回答中引用的部分要自然添加标识,比如[^1][^2]等,但用户提供的引用可能不完全对应,需要根据实际情况调整。例如,引用[4]提到排序,引用[2]提到预防胜于治疗,这些都需要正确标注。 最后,代码示例部分,用户可能希望看到如何模拟死锁以及解决方案的代码。根据引用中的示例,需要确保代码正确,比如使用synchronized关键字,并展示正确的顺序。 现在需要将这些点整合成一个结构清晰的回答,分步骤说明排查方法和解决方案,并添加相关问题。确保使用正确的中文术语,LaTeX格式正确,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能数学公式较少,主要关注代码和文本结构。</think>### Java死锁排查方法与解决方案指南 #### 一、死锁形成条件(需同时满足) 1. 互斥条件:资源独占(如`synchronized`) 2. 请求与保持:持有旧请求新 3. 不可剥夺:只能主动释放 4. 环路等待:多个线程形成环形等待链 #### 二、排查方法(4种实战手段) 1. **jstack工具分析** ```bash jstack <PID> > thread_dump.txt ``` 搜索"deadlock"关键词,定位死锁线程[^1][^3] 2. **JConsole可视化检测** - 连接Java进程后查看"线程"标签 - 检测带有红色警告的线程 3. **代码示例(典型死锁场景)** ```java // 错误示例 Thread1: 定A后请求B Thread2: 定B后请求A ``` 4. **线程转储分析** ```java // 编程式获取线程信息 ThreadMXBean bean = ManagementFactory.getThreadMXBean(); long[] threadIds = bean.findDeadlockedThreads(); ``` #### 三、解决方案(5种核心策略) 1. **统一加顺序** ```java // 正确示例:全局定义顺序 private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); void method() { synchronized(lock1) { synchronized(lock2) { /* 操作 */ } } } ``` 通过预定义顺序消除环路等待[^4] 2. **设置超时机制** ```java ReentrantLock lock = new ReentrantLock(); if (lock.tryLock(300, TimeUnit.MILLISECONDS)) { try { /* 临界区 */ } finally { lock.unlock(); } } ``` 3. **开放调用模式** - 避免在持有时调用外部方法 - 缩小同步代码块范围 4. **死锁检测系统** ```java ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(this::checkDeadlock, 5, 10, SECONDS); ``` 5. **资源分级策略** - 建立的层次结构 - 高层可请求低层,反之禁止 #### 四、预防建议(3个维度) 1. **设计阶段** - 使用`java.util.concurrent`高级API - 优先选择无数据结构 2. **编码规范** - 单次最多获取一个 - 同步块内代码执行时间<1ms 3. **监控体系** - 集成APM工具(如SkyWalking) - 设置线程池监控指标 ```mermaid graph TD A[死锁发生] --> B{jstack分析} B --> C[定位死锁线程] C --> D[检查持有链] D --> E[修改顺序] E --> F[增加超时机制] F --> G[测试验证] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值