您的位置:首页 > 编程语言 > Java开发

【JAVA多线程问题之死锁】

2014-10-02 15:18 357 查看
一、死锁是什么?

举个例子:两个人一起吃饭,每个人都拿了一只筷子,双方都在等待对方将筷子让给自己,结果两个人都吃不了饭。这种情况和计算机中的死锁情况很相似。

假设有两个线程,互相等待对方释放占有的锁,但是释放锁的条件又不可能形成,这时候死锁就形成了。

还是买票的问题,有的时候时会发生死锁的,将以前的代码改动一下,将会产生死锁:

/*
死锁的产生
*/
class Ticket implements Runnable
{
Object obj=new Object();
boolean flag;
private int sum=1000;
public void run()
{
if(flag==true)
{
while(true)
{
synchronized(obj)
{
//->Thread1
show();
}
}
}
else
{
while(true)
show();
}
}
public synchronized void show()
{
//->Thread0
synchronized(obj)
{
if(sum>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
System.out.println(Thread.currentThread().getName()+":function--"+sum--);
}
}
}
}
public class Demo
{
public static void main(String args[])
{
Ticket t=new Ticket();
t.flag=false;
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();

try//加入等待时间,让线程0启动
{
Thread.sleep(10);
}
catch (InterruptedException e)
{

}
t.flag=true;
t2.start();
}
}


死锁的现象:



如图所示,光标停在下一行不断闪烁,没有任何输出。

死锁具体是怎么形成的?以上面的输出为例:

线程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:

class Demo implements Runnable
{
boolean flag;
public Demo(){}
public Demo(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(Sour.lock1)
{
System.out.println("if---------lock1-------");
synchronized(Sour.lock2)
{
System.out.println("if-------lock2------");
}
}
}
}
else
{
while(true)
{
synchronized(Sour.lock2)
{
System.out.println("else---------lock2-------");
synchronized(Sour.lock1)
{
System.out.println("else-------lock1------");
}
}
}
}
}
}
class Sour
{
public static final  Object lock1=new Object();
public static final  Object lock2=new Object();
}
public class DeadLockDemo
{
public static void main(String args[])
{
Demo d1=new Demo(true);
Demo d2=new Demo(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}


死锁代码简单实例二:

class Demo implements Runnable
{
boolean flag;
public Demo(){}
public Demo(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(Sour.lock1)
{
System.out.println("if---------lock1-------");
synchronized(Sour.lock2)
{
System.out.println("if-------lock2------");
}
}
}
}
else
{
while(true)
{
synchronized(Sour.lock2)
{
System.out.println("else---------lock2-------");
synchronized(Sour.lock1)
{
System.out.println("else-------lock1------");
}
}
}
}
}
}
class Sour
{
public static final  Object lock1=new Object();
public static final  Object lock2=new Object();
}
public class DeadLockDemo
{
public static void main(String args[])
{
Demo d1=new Demo();
d1.flag=false;
//Demo d2=new Demo(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d1);

t1.start();
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
d1.flag=true;
t2.start();
}
}


可以观察发现两个代码基本相同,只是主函数中的方法略有不同,其中,第一个代码有99.9%的几率在第三行就锁上;第二个代码要到几百行才能锁上,甚至有时候锁不上。

第一种方法创建了两个实现了Runnable的接口对象,但是不影响结果;和第二个相比,第一个方法排除了不确定因素,是验证死锁程序的有效方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: