java多线程之死锁
一、死锁是什么?
举个例子:两个人一起吃饭,每个人都拿了一只筷子,双方都在等待对方将筷子让给自己,结果两个人都吃不了饭。这种情况和计算机中的死锁情况很相似。
假设有两个线程,互相等待对方释放占有的锁,但是释放锁的条件又不可能形成,这时候死锁就形成了。
还是买票的问题,有的时候时会发生死锁的,将以前的代码改动一下,将会产生死锁:
1 /*
2 死锁的产生
3 */
4 class Ticket implements Runnable
5 {
6 Object obj=new Object();
7 boolean flag;
8 private int sum=1000;
9 public void run()
10 {
11 if(flag==true)
12 {
13 while(true)
14 {
15 synchronized(obj)
16 {
17 //->Thread1
18 show();
19 }
20 }
21 }
22 else
23 {
24 while(true)
25 show();
26 }
27 }
28 public synchronized void show()
29 {
30 //->Thread0
31 synchronized(obj)
32 {
33 if(sum>0)
34 {
35 try
36 {
37 Thread.sleep(10);
38 }
39 catch (InterruptedException e)
40 {
41 }
42 System.out.println(Thread.currentThread().getName()+":function--"+sum--);
43 }
44 }
45 }
46 }
47 public class Demo
48 {
49 public static void main(String args[])
50 {
51 Ticket t=new Ticket();
52 t.flag=false;
53 Thread t1=new Thread(t);
54 Thread t2=new Thread(t);
55 t1.start();
56
57 try//加入等待时间,让线程0启动
58 {
59 Thread.sleep(10);
60 }
61 catch (InterruptedException e)
62 {
63
64 }
65 t.flag=true;
66 t2.start();
67 }
68 }
public class MaiPiao { public static void main(String[] args){ Ticket t=new Ticket(); Thread t1=new Thread(t); Thread t2=new Thread(t); t.flag=false; t1.start(); try { Thread.sleep(10);//加入等待时间,让线程0启动 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t.flag=true; t2.start(); } } class Ticket implements Runnable{ Object obj=new Object(); boolean flag; private int sum; public Ticket(){ this.sum=10; } public void run() { if(flag==true){ while(sum>0){ synchronized(obj){ if(sum>0){ sum--; show(); }else{ show(); } } } }else{ show(); } } public synchronized void show(){ try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(obj){ System.out.println(Thread.currentThread().getName()+" "+sum); } } } /** * * 我对多线程的理解:CPU不定时的在多个线程中转移使用 */ /** * 这个死锁我的解释: * 首先一个线程走正路,也就是执行while(sum>0)中进入第一道锁,取得obj使用权,强行将其睡眠一段时间让线程2进入show(),准备执行show(), * show()需要Tickets对象作为第一把锁的钥匙,然后进入后还需要obj对象解第二道锁 * 第二个线程走偏路,也就是直接执行show(),解开第一道锁,占据Ticket对象,无法继续,因为obj使用权在线程1 * 线程1到了show()入口没有Ticket使用权无法进入 * 这样就造成了死锁 */
死锁的现象:

如图所示,光标停在下一行不断闪烁,没有任何输出。
死锁具体是怎么形成的?以上面的输出为例:
线程0启动之后,由于标志变量为false,所以走的是else块,进入死循环,调用show方法,拿到this锁,然后继续拿到obj锁,顺利执行完毕之后,依次释放obj锁、this锁,然后开始进行第二次循环,拿到了this锁,但是将要拿到obj锁的时候,CPU切换到了线程1,线程1启动之后,由于flag已经是true,所以走的是if块,拿到了obj锁,刚要拿this锁,CPU切换到了线程0,线程0已经拿到了this锁,所以开始请求obj锁,但是obj锁在线程1手里,所以CPU切换到了线程1;线程1已经拿到了obj锁,所以它开始请求this锁,但是this锁在线程0手里,于是CPU切换到了线程0;..................
我们经过上面的分析,由于两个线程手里各自拿着对方的锁,相互请求但是没有让其中一方先释放锁的条件,所以CPU在两个线程之间不断切换,但是不会执行任何一方的任务。
总结死锁产生的条件:
1.有两个或者两个以上的锁。
2.同步嵌套(锁的嵌套)
死锁代码简单实例1:
1 class Demo implements Runnable
2 {
3 boolean flag;
4 public Demo(){}
5 public Demo(boolean flag)
6 {
7 this.flag=flag;
8 }
9 public void run()
10 {
11 if(flag)
12 {
13 while(true)
14 {
15 synchronized(Sour.lock1)
16 {
17 System.out.println("if---------lock1-------");
18 synchronized(Sour.lock2)
19 {
20 System.out.println("if-------lock2------");
21 }
22 }
23 }
24 }
25 else
26 {
27 while(true)
28 {
29 synchronized(Sour.lock2)
30 {
31 System.out.println("else---------lock2-------");
32 synchronized(Sour.lock1)
33 {
34 System.out.println("else-------lock1------");
35 }
36 }
37 }
38 }
39 }
40 }
41 class Sour
42 {
43 public static final Object lock1=new Object();
44 public static final Object lock2=new Object();
45 }
46 public class DeadLockDemo
47 {
48 public static void main(String args[])
49 {
50 Demo d1=new Demo(true);
51 Demo d2=new Demo(false);
52 Thread t1=new Thread(d1);
53 Thread t2=new Thread(d2);
54 t1.start();
55 t2.start();
56 }
57 }
死锁代码简单实例二:
1 class Demo implements Runnable
2 {
3 boolean flag;
4 public Demo(){}
5 public Demo(boolean flag)
6 {
7 this.flag=flag;
8 }
9 public void run()
10 {
11 if(flag)
12 {
13 while(true)
14 {
15 synchronized(Sour.lock1)
16 {
17 System.out.println("if---------lock1-------");
18 synchronized(Sour.lock2)
19 {
20 System.out.println("if-------lock2------");
21 }
22 }
23 }
24 }
25 else
26 {
27 while(true)
28 {
29 synchronized(Sour.lock2)
30 {
31 System.out.println("else---------lock2-------");
32 synchronized(Sour.lock1)
33 {
34 System.out.println("else-------lock1------");
35 }
36 }
37 }
38 }
39 }
40 }
41 class Sour
42 {
43 public static final Object lock1=new Object();
44 public static final Object lock2=new Object();
45 }
46 public class DeadLockDemo
47 {
48 public static void main(String args[])
49 {
50 Demo d1=new Demo();
51 d1.flag=false;
52 //Demo d2=new Demo(false);
53 Thread t1=new Thread(d1);
54 Thread t2=new Thread(d1);
55
56 t1.start();
57 try
58 {
59 Thread.sleep(10);
60 }
61 catch (InterruptedException e)
62 {
63 }
64 d1.flag=true;
65 t2.start();
66 }
67 }
可以观察发现两个代码基本相同,只是主函数中的方法略有不同,其中,第一个代码有99.9%的几率在第三行就锁上;第二个代码要到几百行才能锁上,甚至有时候锁不上。
第一种方法创建了两个实现了Runnable的接口对象,但是不影响结果;和第二个相比,第一个方法排除了不确定因素,是验证死锁程序的有效方法。


浙公网安备 33010602011771号