这个例子个人觉得非常经典,先看完,讲解在下面啦,(^__^) 嘻嘻!
/*
* 死锁:
* 同步中嵌套同步,而锁不同。
* 都在等待对方线程中的锁释放。
* */
public class Demo8 {
public static void main(String[] args) {
// 创建了一个ticket对象
Ticket2 t = new Ticket2();
// 创建了2个线程
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
// 启动线程t1
t1.start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.flag = false;
// 启动线程t2
t2.start();
}
}
class Ticket2 implements Runnable {
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run() {
if (flag) {
while (true) {
synchronized (obj) {
System.out.println("cccc");
// 调用show
show();
}
}
} else {
while (true) {
System.out.println("ddddd");
show();
}
}
}
public synchronized void show() {// 这里有两把锁 函数(this) obj
System.out.println("aaaa");
synchronized (obj) {
System.out.println("bbbb");
if (tick > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()
+ "code...." + tick--);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
结果为(各种可能,这个可能是我特意多次运行等到的结果,因为这个结果讲起来才更具代表性):并手动对其进行了一个编号
cccc 1
aaaa 2
bbbb 3
ddddd 4
Thread-0code....100 5
cccc 7
aaaa 8
bbbb 9
Thread-0code....99 10
cccc 11
aaaa 12
现在对其产生的原因进行详细讲解:
首先创建了两个线程(新生态),t1先启动,flag为true,那么obj先上锁,输出cccc,然后调用show()方法,这里上了第二道锁,输出了aaaa,因为有相同的obj的锁,所以可以进入输出bbbb,为何后面是ddddd呢,因为bbbb之后休眠了10毫秒此时flag改为了false后,t2启动执行else中的代码所以才有的ddddd,休眠结束后输出Thread-0code。。。。100。对于if中始终都持有obj这把锁(预测很容易发生死锁,马上就要了,(^__^) 嘻嘻,坚持看下去),接下来的cccc是因为被t1抢到了,然后是因为t1还猛些,抢到了第二把锁synchronized,同理输出aaaa、bbbb,然后输出“Thread-0code…..99”,(关键点马上就来了,马上死锁了,最后两行代码了还剩),此时t1还是有obj这把锁,t1继续执行while循环,先遇到synchronized(obj),可以进入,因为obj当前在t2手上,输出cccc后即可就要进入show()方法了。但是不幸的是,(show()方法有synchronized关键字修饰,表示该方法有锁)被t2抢到了show这把锁,进入show()后立刻上锁,输出aaaa,可是现在t2需要obj这把锁,可是这把锁t1拿着的,所以需要等待t1释放obj这把锁,然而t1等待着show的这把锁释放呢,所以两个就在相互的等待中了,从而进入了传说中的死锁状态。
可能各位看着上面的话语太多,并且前面的“结果中的行号”并没有什么乱用,因为实在不好描述(不能用画画的方式即时演示),可是若是一点一点的看的话,将会对死锁有非常透彻的理解,希望各位看到此段话的时候,能够抱着一定要理解“死锁”的心态去阅读。有何问题在评论中留言,我会在2个工作日内回复的。