首页 > 代码库 > 【JAVA多线程问题之死锁】
【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 //->Thread118 show();19 }20 }21 }22 else23 {24 while(true)25 show();26 }27 }28 public synchronized void show()29 {30 //->Thread031 synchronized(obj)32 {33 if(sum>0)34 {35 try36 {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 Demo48 {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 }
死锁的现象:
如图所示,光标停在下一行不断闪烁,没有任何输出。
死锁具体是怎么形成的?以上面的输出为例:
线程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 else26 {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 Sour42 {43 public static final Object lock1=new Object();44 public static final Object lock2=new Object();45 }46 public class DeadLockDemo47 {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 else26 {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 Sour42 {43 public static final Object lock1=new Object();44 public static final Object lock2=new Object();45 }46 public class DeadLockDemo47 {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 try58 {59 Thread.sleep(10);60 }61 catch (InterruptedException e)62 {63 }64 d1.flag=true;65 t2.start();66 }67 }
可以观察发现两个代码基本相同,只是主函数中的方法略有不同,其中,第一个代码有99.9%的几率在第三行就锁上;第二个代码要到几百行才能锁上,甚至有时候锁不上。
第一种方法创建了两个实现了Runnable的接口对象,但是不影响结果;和第二个相比,第一个方法排除了不确定因素,是验证死锁程序的有效方法。
【JAVA多线程问题之死锁】