`
小懒蛋
  • 浏览: 31474 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

java 并发死锁产生原因与诊断

阅读更多

一、 死锁的产生原因

并发死锁产生的原因一般是由于加锁顺序不一致引起的,假设一个事物需要获得连续获得两个资源的锁,如果执行事物的两个线程获得这两个锁的顺序不一致,就有可能产生死锁。


下面是我画的一个简单的说明图:



 

 在Dead lock point, 第一个线程拥有A锁,想获得B锁, 第二个线程拥有B锁,想获得A锁,两个线程相互拥有另一个线程所等待的资源。

造成这种局面的原因就是两个线程加锁的顺序不一样,一旦程序中产生死锁,唯一能做的就是杀死进程,重启应用。

 

二、用java代码实现死锁

 

运行以下代码,程序会一直运行,不退出。

 

 

package concurrent.deadlock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by vincent on 2015/9/3.
 */
public class TestDeadLock {
    private static Lock lockA = new ReentrantLock();
    private static Lock lockB = new ReentrantLock();

    public static void main(String args[]){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    lockA.lock();
                    TimeUnit.SECONDS.sleep(2);
                    try{
                        lockB.lock();
                    }finally {
                        lockB.unlock();
                    }
                }catch (InterruptedException e) {

                }finally {
                    lockA.unlock();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    lockB.lock();
                    TimeUnit.SECONDS.sleep(2);
                    try{
                        lockA.lock();
                    }finally {
                        lockA.unlock();
                    }
                }catch (InterruptedException e) {

                }finally {
                    lockB.unlock();
                }
            }
        }).start();
    }
} 

 代码链接: https://github.com/HongkaiWen/study/blob/master/src/main/java/concurrent/deadlock/TestDeadLock.java

 

三、程序产生死锁后的排查

 

产生死锁的现象就是程序停在那里,不会对调用者产生反馈。以我上边写的例子来说,就是程序无法退出。

下面以我这个例子来说明如何确定程序是因为死锁才停在那里。(这个程序是我故意写的死锁,一般的应用中是没有人会故意写死锁的,所以程序发生阻塞时,需要定位问题的原因)

 

windows平台:

我在我本机windows平台下运行我的死锁程序, 程序停在那里不动。

这时我打开jvisualvm.exe (jdk的bin目录下),这是一个图形界面程序,截图如下:

 

 

关于此界面的说明:

首先左侧是一个JVM的进程列表,在列表中我可以找到我的应用的进程,单击目标进程即可查看进程详细信息。

右边很明显了,红色字体提示检测到死锁,点击线程Dump就可以查看进一步的详细信息了。

下面是一条一条的时间线是所有线程的状态了,Thread-1 和 Thread-2是我们的用户线程,黄色代表等待状态。

 

下面是线程dump的信息,信息比较多,我贴出关键部分:

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting for ownable synchronizer 0x09df1d10, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-0"
"Thread-0":
  waiting for ownable synchronizer 0x09df1ef8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-1"

 意思就是线程1等待线程2持有的资源,线程2等待线程1持有的资源,再下面的信息可以定位到具体代码的哪一行:

"Thread-1":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x09df1d10> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at concurrent.TestDeadLock$2.run(TestDeadLock.java:40)
	at java.lang.Thread.run(Thread.java:745)
"Thread-0":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x09df1ef8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
	at concurrent.TestDeadLock$1.run(TestDeadLock.java:22)
	at java.lang.Thread.run(Thread.java:745)

 

linux平台

 一般linux平台不太方便用图形界面的工具,而一般java的程序都是部署在linux平台上的,这种环境程序发生阻塞时要怎么办呢,和windows思路是一样的,只不过是工具不同而已。

首先我在linux上运行我的java程序,程序如期卡住。



 

这时,我运行进程查看命令,找到我的进程:



 

此时可以通过jstack命令导出jvm的栈调用信息:

 


 
 

 下面是stack.txt中导出的信息(部分):

 找到了和之前windows一样的信息。

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting for ownable synchronizer 0x00000000d6c68848, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-0"
"Thread-0":
  waiting for ownable synchronizer 0x00000000d6c68878, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000d6c68848> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:867)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:214)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:290)
	at concurrent.deadlock.TestDeadLock$2.run(TestDeadLock.java:40)
	at java.lang.Thread.run(Thread.java:745)
"Thread-0":
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000d6c68878> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:867)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:214)
	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:290)
	at concurrent.deadlock.TestDeadLock$1.run(TestDeadLock.java:22)
	at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

 

 四、死锁程序的预防与处理

 避免 死锁的方法就是不要写 顺序不一致加锁 的代码

 如果 必须要写顺序不一致的加锁的代码,可以采用 trylock 方法,避免程序一直等待。

 预防 死锁可以采用finddebug插件去扫描代码发现死锁。

 处理 死锁,如果运行中的程序已经发生死锁,据我所知没有别的办法,只能停止程序,代价非常大,所以要尽量避免死锁。

 

关于死锁的避免与预防有时间我再整理一下分享一下我的心得。

关于并发编程,想要写出正确可靠高性能的程序实在比较难,我知道的也是一些皮毛,欢迎大家批评指正,互相学习。

如需转载请注明出处:  http://waitingkkk-163-com.iteye.com/blog/2240412

 

 

 

  • 大小: 26.2 KB
  • 大小: 49.2 KB
  • 大小: 1.8 KB
  • 大小: 5.5 KB
  • 大小: 15 KB
分享到:
评论

相关推荐

    Java 并发编程实战.pdf

    在《Java并发编程实战》中,读者也能够了解到如何将并发与现代Java语言特性结合起来,例如使用Lambda表达式和Stream API来简化并发代码的编写。 综合来看,该书不仅适合于对Java并发编程感兴趣的初学者,同样也适合...

    ( Java并发程序设计教程.zip )高清版 PDF

    首先,书中会讲解Java并发的基础知识,包括线程的创建、启动、同步与通信。Java通过Thread类和Runnable接口提供线程支持,读者将学习如何创建和管理线程。同步机制如synchronized关键字、volatile变量以及java.util....

    Java并发程序设计教程

    本教程将深入探讨Java并发编程的核心概念、最佳实践以及常见陷阱。 首先,我们要了解Java中的线程。线程是操作系统分配CPU时间的基本单元,Java通过Thread类来抽象线程。创建线程有两种方式:继承Thread类并重写run...

    java并发编程实战高清版pdf

    《Java并发编程实战》是Java开发者深入理解和掌握并发编程的一本经典著作。这本书全面地介绍了Java平台上的并发和多线程编程技术,旨在帮助开发者在多核时代编写出高效、可伸缩且线程安全的代码。 并发编程是现代...

    Java并发编程:设计原则与模式2中文版

    首先,书中详细介绍了Java并发编程的基础知识,包括线程的创建与管理、死锁、活锁和饥饿等问题,以及如何避免和解决这些问题。同时,它讲解了Java内存模型(JMM)和volatile、synchronized关键字的作用,这些都是...

    java并发编程实战.zip

    1. **Java并发基础**:介绍Java并发编程的基础知识,包括线程的创建与使用、线程的状态模型(新建、运行、阻塞、等待、终止)以及Java中的Thread类和Runnable接口。 2. **同步机制**:讲解了Java中的基本同步机制,...

    Java并发编程实践(Java Concurrency in Practice) (中英版)

    3. **并发集合与并发容器**:涵盖了Java并发集合框架,包括线程安全的ArrayList、LinkedList、HashMap等,并介绍了ConcurrentHashMap、CopyOnWriteArrayList等高效率的并发容器。 4. **并发工具**:讨论了Executor...

    Java并发编程实践(英文非扫描版-带书签目录)

    9. 并发问题的诊断与调试:提供了诊断多线程程序问题的工具与技巧,如日志分析、线程转储分析等。 10. Java并发编程的未来方向:探讨了并发编程的未来趋势,包括异步编程模型、函数式编程在并发编程中的应用等。 ...

    java并发编程实践

    本书首先介绍了Java并发的基础,包括线程的创建与管理、同步机制如synchronized和volatile关键字,以及线程安全的数据结构。这些基础知识对于理解并发编程至关重要,因为它们帮助开发者避免常见的并发问题,如竞态...

    [Java并发编程实践].(Java.Concurrency.in.Practice).Brian.Goetz.文字版(1)

    《Java并发编程实践》是Java并发领域的一本经典著作,由Brian Goetz等多位专家合著。这本书深入探讨了如何在Java环境中有效地进行多线程和并发编程,以充分利用现代多核处理器的性能。以下是对本书核心知识点的详细...

    Java 并发编程实战

    - **死锁**:分析死锁产生的原因,以及避免和检测死锁的方法。 6. **并发性能调优** - **监控与诊断**:介绍JDK自带的监控和诊断工具,如jconsole、VisualVM等,用于分析和优化并发程序。 - **并发性能指标**:...

    JAVA并发编程实践

    《JAVA并发编程实践》这本书深入探讨了Java平台上的并发编程技术,旨在帮助开发者理解并有效利用多线程环境,提高程序的效率和响应性。在Java并发编程中,有许多关键概念和技术,它们对于构建可扩展、高效且健壮的多...

    Java并发编程实战.pdf

    Java并发编程中常见的问题之一是死锁,即两个或多个线程因争夺资源而造成的一种僵局。为了防止死锁,可以采用多种策略,包括避免获取多个锁时的死锁问题,如确保以相同的顺序获取锁,以及使用死锁检测工具来诊断问题...

    Java并发编程实践

    - JConsole与VisualVM:Java并发调试工具的使用。 这些章节涵盖了Java并发编程的各个方面,从基础概念到高级应用,旨在帮助开发者构建高效率、高可靠性的并发程序。理解和掌握这些知识点,对于提升Java开发者的...

    Java并发编程(学习笔记).xmind

    死锁的避免与诊断 支持定时的显示锁 通过线程转储信息来分析死锁 其他活跃性危险 饥饿 要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都...

    Java并发编程实践 全集

    《Java并发编程实践》全集是一本深入探讨Java平台上的多线程和并发编程的权威著作。这本书旨在帮助开发者理解并有效地使用Java并发工具,从而编写出高效、可靠的多线程程序。PDF格式使得读者可以方便地在电子设备上...

Global site tag (gtag.js) - Google Analytics