Java避免死锁的几个常见方法(有测试代码和分析过程)

目录

Java避免死锁的几个常见方法

死锁产生的条件

上死锁代码

然后 :jstack 14320 >> jstack.text

Java避免死锁的几个常见方法


Java避免死锁的几个常见方法

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

死锁产生的条件

  1. 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
  2. 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
  3. 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
  4. 环路等待条件:是指进程发生死锁后,必然存在一个进程–资源之间的环形链死锁问题

上死锁代码

package cn.net.cdsz.ccb.test;

public class TestDeadLock {

    private static Object obj1 = new Object();
    private static Object obj2 = new Object();

    public static void main(String[] args) {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
    }

    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            synchronized (obj1) {
                System.out.println("Thread1 拿到 obj1 的锁");
                try {
                    //停顿2秒的意义在于,让thread2线程拿到obj2的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (obj2) {
                    System.out.println("Thread1 拿到 obj2 的锁");
                }
            }

        }
    }

    private static class Thread2 implements Runnable {
        @Override
        public void run() {
            synchronized (obj2) {
                System.out.println("Thread2 拿到 obj2 的锁");
                try {
                    //停顿2秒的意义在于,让thread1线程拿到obj1的锁
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (obj1) {
                    System.out.println("Thread2 拿到 obj1 的锁");
                }
            }
        }
    }


}

一直等待下了。。。

 去找pid:14320

然后 :jstack 14320 >> jstack.text

得到以下内容:

2023-04-14 14:51:33
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.31-b07 mixed mode):

"JMX server connection timeout 21" #21 daemon prio=5 os_prio=0 tid=0x000000002167d800 nid=0x6108 in Object.wait() [0x00000000225ef000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
	- locked <0x000000076e3b32b8> (a [I)
	at java.lang.Thread.run(Thread.java:745)

"RMI Scheduler(0)" #20 daemon prio=5 os_prio=0 tid=0x0000000021674800 nid=0x3d78 waiting on condition [0x00000000224ee000]
   java.lang.Thread.State: TIMED_WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076dc34d08> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
	at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
	at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

"RMI TCP Connection(1)-192.168.136.113" #19 daemon prio=5 os_prio=0 tid=0x0000000021a3b800 nid=0x10c0 runnable [0x00000000223ee000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.read(SocketInputStream.java:150)
	at java.net.SocketInputStream.read(SocketInputStream.java:121)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
	- locked <0x000000076e281978> (a java.io.BufferedInputStream)
	at java.io.FilterInputStream.read(FilterInputStream.java:83)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$240(TCPTransport.java:683)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1/410101441.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

"RMI TCP Accept-0" #18 daemon prio=5 os_prio=0 tid=0x00000000212dc000 nid=0x8584 runnable [0x000000000153e000]
   java.lang.Thread.State: RUNNABLE
	at java.net.DualStackPlainSocketImpl.accept0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
	at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:404)
	at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
	- locked <0x000000076dc69d98> (a java.net.SocksSocketImpl)
	at java.net.ServerSocket.implAccept(ServerSocket.java:545)
	at java.net.ServerSocket.accept(ServerSocket.java:513)
	at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(LocalRMIServerSocketFactory.java:52)
	at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:400)
	at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:372)
	at java.lang.Thread.run(Thread.java:745)

"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x000000000365b000 nid=0xae0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #15 prio=5 os_prio=0 tid=0x000000001ec17000 nid=0x69ac waiting for monitor entry [0x0000000020a1f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at cn.net.cdsz.ccb.test.TestDeadLock$Thread2.run(TestDeadLock.java:46)
	- waiting to lock <0x000000076c88add0> (a java.lang.Object)
	- locked <0x000000076c88ade0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

"Thread-0" #14 prio=5 os_prio=0 tid=0x000000001ec24800 nid=0x8d08 waiting for monitor entry [0x000000002091f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at cn.net.cdsz.ccb.test.TestDeadLock$Thread1.run(TestDeadLock.java:26)
	- waiting to lock <0x000000076c88ade0> (a java.lang.Object)
	- locked <0x000000076c88add0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

"Service Thread" #13 daemon prio=9 os_prio=0 tid=0x000000001eb16000 nid=0x8ac8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

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

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

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

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

"JDWP Command Reader" #8 daemon prio=10 os_prio=0 tid=0x000000001ea53800 nid=0x3634 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #7 daemon prio=10 os_prio=0 tid=0x000000001ea4d800 nid=0x86e0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #6 daemon prio=10 os_prio=0 tid=0x000000001ea41000 nid=0x540 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

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

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

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001d2eb800 nid=0x8670 in Object.wait() [0x000000001fd1f000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x000000076c3862f8> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:142)
	- locked <0x000000076c3862f8> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:158)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001d2ea000 nid=0x8ba4 in Object.wait() [0x000000001fc1e000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x000000076c385d68> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)
	- locked <0x000000076c385d68> (a java.lang.ref.Reference$Lock)

"VM Thread" os_prio=2 tid=0x000000001e9a1800 nid=0x6398 runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000003670000 nid=0x7e64 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000003671800 nid=0x8218 runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000003673000 nid=0x7830 runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000003674800 nid=0x8e8c runnable 

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000003676800 nid=0x8114 runnable 

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000003678000 nid=0x8220 runnable 

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000000000367b000 nid=0x6fc0 runnable 

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000000000367c000 nid=0x26d8 runnable 

"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x000000000367d800 nid=0x8238 runnable 

"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x000000000367e800 nid=0x82a4 runnable 

"VM Periodic Task Thread" os_prio=2 tid=0x000000001eb18000 nid=0x8900 waiting on condition 

JNI global references: 4894


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001d2f21b8 (object 0x000000076c88add0, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001d2ef668 (object 0x000000076c88ade0, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at cn.net.cdsz.ccb.test.TestDeadLock$Thread2.run(TestDeadLock.java:46)
	- waiting to lock <0x000000076c88add0> (a java.lang.Object)
	- locked <0x000000076c88ade0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)
"Thread-0":
	at cn.net.cdsz.ccb.test.TestDeadLock$Thread1.run(TestDeadLock.java:26)
	- waiting to lock <0x000000076c88ade0> (a java.lang.Object)
	- locked <0x000000076c88add0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

以上jstack.text内容的最后:

可以清晰的看到:

Thread-1获取了 <0x000000076c88ade0> 的锁,等待获取 <0x000000076c88add0> 这个锁
Thread-0获取了 <0x000000076c88add0> 的锁,等待获取 <0x000000076c88ade0> 这个锁
由此可见,发生了死锁。
 

避免死锁的几个常见方法

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

参考:【JVM】面试题之死锁及问题是怎么定位_jvm线程锁定定位_it噩梦的博客-CSDN博客

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
死锁是多线程编程中的一种常见问题,当多个线程持有彼此所需的锁时,就可能会发生死锁。为了避免死锁,可以采取以下几种方法: 1. 避免嵌套锁 嵌套锁是死锁的一个常见原因。为了避免嵌套锁,可以尽量减少使用`synchronized`关键字,或者使用`ReentrantLock`等可重入锁。 2. 按照固定的顺序获取锁 如果多个线程需要获取多个锁,为了避免死锁,可以规定获取锁的顺序。例如,如果线程A需要获取锁1和锁2,线程B需要获取锁2和锁1,那么可以规定所有线程都必须按照锁的编号从小到大的顺序获取锁,从而避免死锁的发生。 3. 设置超时时间 在获取锁时,可以设置一个超时时间,如果在该时间内无法获取到锁,就放弃该锁,从而避免死锁。可以使用`tryLock(long time, TimeUnit unit)`方法来设置超时时间。 4. 使用Lock.tryLock()方法 `Lock`接口提供了`tryLock()`方法,该方法可以尝试获取锁,如果获取成功,则返回`true`,否则返回`false`。可以在获取锁前先调用`tryLock()`方法,如果获取失败,则可以放弃该锁。 5. 尽量减少同步代码块的长度 同步代码块的执行时间越长,就越容易引发死锁。因此,可以尽量减少同步代码块的长度,避免在同步代码块中执行耗时操作。 需要注意的是,死锁是一种难以排查和解决的问题,如果遇到死锁,可以通过线程转储、日志分析等方式来进行排查。同时,在编写多线程程序时,应该尽量避免出现死锁等问题,从而保证程序的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值