多线程中的死锁举例与分析(转)

1. 一个特殊构造的程序
考虑下面这个专门为说明多线程中的死锁现象而构造的程序:

import java.util.LinkedList;

public class Stack {
    public static void main(String[] args) {
        final Stack stack = new Stack();
        new Thread("push") {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    try {
                        Thread.sleep(10);//to make the deadlock occur
                    } catch (InterruptedException e) {
                    }
                    stack.push("object " + i);
                }
            }
        }.start();

        new Thread("pop") {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    try {
                        System.out.println(stack.pop());
                    } catch (Exception e) {
                    }
                }
            }
        }.start();
    }

    LinkedList<Object> list = new LinkedList<Object>();

    public synchronized void push(Object x) {
        System.out.println("begin to push " + x);
        synchronized (list) {
            list.addLast(x);
            notify();
        }
        System.out.println("end to push " + x);
    }

    public synchronized Object pop() throws Exception {
        System.out.println("begin to pop");
        synchronized (list) {
            if (list.size() <= 0) {
                wait();
            }
            return list.removeLast();
        }
    }

}

该程序构造了一个 Stack,启动了两个线程。一个线程向 Stack 中添加数据,另外一个线程从 Stack 中取出数据并打印。
但是运行程序后就会发现程序输出:

begin to pop
begin to push object 0

后,在再也没有后续输出了。

2. Dump 并分析线程状态
启动 jvisualvm 查看该程序线程的状态,将其 Dump,就可以得到以下线程堆栈信息:

"pop" prio=6 tid=0x00c00000 nid=0x2b0 in Object.wait() [0x00f9f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x27f65648> (a loggerlock.Stack)
    at java.lang.Object.wait(Object.java:485)
    at loggerlock.Stack.pop(Stack.java:49)
    - locked <0x27f65658> (a java.util.LinkedList)
    - locked <0x27f65648> (a loggerlock.Stack)
    at loggerlock.Stack$2.run(Stack.java:26)

   Locked ownable synchronizers:
    - None

"push" prio=6 tid=0x00bfec00 nid=0x14c8 waiting for monitor entry [0x00f4f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at loggerlock.Stack.push(Stack.java:39)
    - waiting to lock <0x27f65658> (a java.util.LinkedList)
    - locked <0x27f65648> (a loggerlock.Stack)
    at loggerlock.Stack$1.run(Stack.java:16)

   Locked ownable synchronizers:
    - None

可以看到,pop 线程正在运行 wait(); 语句,处于 WAITING 状态,同时,该线程锁住了 list 和 stack 对象。
push 线程处于 BLOCKED 状态,等待其他线程释放 list 对象。

3. 运行过程及死锁原因分析


步骤


主程序


pop 线程


push 线程


1


启动

   

2


创建 stack 对象

   

3


创建 list 对象

   

4

 
启动

 

5

   
启动


6

   
sleep 10ms


7

 
调用 stack.pop()

 

8

 
锁住 stack 对象

 

9

 
打印 "begin to pop"

 

10

 
锁住 list 对象

 

11

 
调用 stack.wait()
(暂时释放 stack 对象)

 

12

   
锁住 stack 对象


13

   
打印 "begin to push 0"


14

   
企图锁住 list 对象
(发现 list 已被其他线程锁住)


15


进入死锁状态

http://www.educity.cn/it/sun/201004191051041573.htm

时间: 2024-10-23 07:20:35

多线程中的死锁举例与分析(转)的相关文章

Java多线程中的死锁问题[转]

//申明:本文转载自http://www.cnblogs.com/digdeep/p/4448148.html <Java多线程中的死锁问题> Java程序基本都要涉及到多线程,而在多线程环境中不可避免的要遇到线程死锁的问题.Java不像数据库那么能够检测到死锁,然后进行处理,Java中的死锁问题,只能通过程序员自己写代码时避免引入死锁的可能性来解决. 1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得

Java多线程中的死锁问题

Java程序基本都要涉及到多线程,而在多线程环境中不可避免的要遇到线程死锁的问题.Java不像数据库那么能够检测到死锁,然后进行处理,Java中的死锁问题,只能通过程序员自己写代码时避免引入死锁的可能性来解决. 1. Java中导致死锁的原因 Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了.导致了死锁.这是最容易理解也是最简单的死锁的形式.但是实际环境中的死锁往往

java多线程中的死锁

产生死锁的原因:多个线程用到了多个锁,多个锁之间存在交叉关系,就有可能产生死锁. 下面是简单的死锁,下面代码中的死锁不是一定会产生. package demo_1; public class TestDeadLock { public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); Thread t1 = new Thread(thread1);

多线程中解决死锁的问题

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //主队列异步 //    [self mainQueueAsync]; //主队列同步 //    [self mainQueueSync]; //解决死锁 [self demo]; } //解决死锁 - (void)demo { NSLog(@"begin"); //异步 dispatch_async(dispatc

[转载]DllMain中不当操作导致死锁问题的分析——线程中调用GetModuleFileName、GetModuleHandle等导致死锁

(转载于breaksoftware的csdn博客) 之前的几篇文章已经讲解了在DllMain中创建并等待线程导致的死锁的原因.是否还记得,我们分析了半天汇编才知道在线程中的死锁位置.如果对于缺乏调试经验的同学来说,可能发现这个位置有点麻烦.那么本文就介绍几个例子,它们会在线程明显的位置死锁掉. DLL中的代码依旧简单.它获取叫EVENT的命名事件,然后等待这个事件被激活.激活的操作自然放在线程中.这次我们不用在DLL中创建线程,而是在Exe中创建. switch (ul_reason_for_c

[转载]DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子2

(转载于breaksoftware的csdn博客) 本文介绍使用Windbg去验证<DllMain中不当操作导致死锁问题的分析--导致DllMain中死锁的关键隐藏因子>中的结论,调试对象是文中刚开始那个例子. 1 g 让程序运行起来 2 ctrl+break 中断程序 3 ~ 查看线程数 其实该程序自己运行起来的线程只有ID为0.TID为afc的线程.18c4线程是我们在windbg中输入ctrl+break,导致windbg在我们调试的进程中插入的一个中断线程.以后我们看到是这个线程的操作

iOS多线程中,队列和执行的排列组合结果分析

本文是对以往学习的多线程中知识点的一个整理. 多线程中的队列有:串行队列,并发队列,全局队列,主队列. 执行的方法有:同步执行和异步执行.那么两两一组合会有哪些注意事项呢? 如果不是在董铂然博客园看到这边文章请 点击查看原文 提到多线程,也就是四种,pthread,NSthread,GCD,NSOperation 其中phtread是跨平台的.GCD和NSOperation都是常用的,后者是基于前者的. 但是两者区别:GCD的核心概念是将一个任务添加到队列,指定任务执行的方法,然后执行. NSO

[转]DllMain中不当操作导致死锁问题的分析&mdash;&mdash;DllMain中要谨慎写代码(完结篇)

在CSDN中发现这篇文章,讲解的比较详细,所以在这里备份一个.原文链接:http://blog.csdn.net/breaksoftware/article/details/8167641 DllMain的相关特性 首先列出<DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析>中论证的11个特性: Dll的加载不会导致之前创建的线程调用其DllMain函数. 线程创建后会调用已经加载了的DLL的DllMain,且调用原因是DLL_THREAD_ATTA

Java多线程7:死锁

前言 死锁单独写一篇文章是因为这是一个很严重的.必须要引起重视的问题.这不是夸大死锁的风险,尽管锁被持有的时间通常很短,但是作为商业产品的应用程序每天可能要执行数十亿次获取锁->释放锁的操作,只要在这数十亿次操作中只要有一次发生了错误,就可能导致程序中发生死锁,并且即使通过压力测试也不可能找出所有潜在的死锁. 死锁 一个经典的多线程问题. 当一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞,这个我们都知道.如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得