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-27 14:49:21