黑马程序员——多线程5:死锁

本文通过售票系统的示例,深入浅出地介绍了Java多线程中死锁现象的产生原因及其实现方式。通过两个线程争夺不同的锁资源,展示了如何在特定条件下触发死锁,并提供了一段易于理解的代码示例。

------- android培训java培训、期待与您交流! ----------

        我们再次利用售票的例子来说明死锁现。为了观察到死锁现象,在if和else代码块中的分别设置两个相同的“锁”对象,但是两个“锁”的位置不同。代码的其他部分与之前的实例基本一致。

代码1:

class Tickets implements Runnable
{
	private int count = 10000;
	boolean flag = true;
	Object obj = new Object();
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				/*
					为观察到死锁现象,这里故意设置两个不同的锁
					并且if和else代码块中所得位置相反
				*/
				synchronized(obj)
				{
					synchronized(this)
					{
						if(count> 0)
						{    
							try{Thread.sleep(10);}catch(Exceptione){}
							System.out.println(Thread.currentThread().getName()+"___同步代码块___"+count--);
						}
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(this)
				{
					synchronized(obj)
					{
						sellTickets();
					}
				}
			}
		}
	}
	public void setFlag(boolean flag)
	{
		this.flag = flag;
	}
	private void sellTickets()
	{
		if(count> 0)
		{
			try{Thread.sleep(10);}catch(Exceptione){}
			System.out.println(Thread.currentThread().getName()+"_________普通方法_________"+count--);
		}
	}
}
class DeadLock
{
	public static void main(String[] args)
	{
		Tickets ts = new Tickets();
 
		Thread t1 = new Thread(ts);
		Thread t2 = new Thread(ts);
             
		t1.start();
             
		try{Thread.sleep(10);}catch(Exceptione){}
		ts.setFlag(false);
 
		t2.start();
	}
}
多次执行的结果表明:通常,两个线程都可以正常开启,但是,很多时候执行到一半(也就是说票数并没有为0),在没有人为停止的情况下,程序就停止执行了,这就是死锁现象。

       我们来设想这么一个情景:1号和2号线程均被开启并正常执行了一段时间,1号线程执行if代码块,2号线程执行else代码块。在某一个时刻1号线程恰好判断完Object对象(简称obj,下同)锁所在同步代码块以后,将obj锁锁住。大家注意,obj锁一旦锁上就表示所有使用该锁的同步代码块都将不能被访问,直到1号线程“打开”obj锁,也就是说此时else代码块中的第二层同步代码块也将不能被访问。

       接着,1号线程打算继续执行if语句中的第二层同步代码块,也就是说判断this锁。但是,此时2号线程也刚好判断完this锁后锁上了this,并打算继续执行第二层同步代码块,判断obj锁。

       此时就发生了一件很奇妙的事情:1号线程想打开它所在代码的this锁,但被2号线程锁住了;2号线程向打开它所在代码的obj锁,却被1号线程锁住了,此时谁也无法继续执行,那么两个锁就都不能打开,程序就停止了运行,这就是死锁现象。

       当然在实际开发中,我们要尽量避免死锁,那么首先就要理解死锁的发生请将,并可以自己模拟出来,这也是一些面试中的试题,那么我们依照代码1的思路在写一个更为一般的死锁的代码。

代码2:

class Test implements Runnable
{
	//由传入的标记值确定该线程执行if-else哪个代码块
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				//if-else代码块中的两个锁位置颠倒
				synchronized(MyLock.lock1)
				{
					//每打开一个锁打印对应字符串,以便于观察
					System.out.println(Thread.currentThread().getName()+":if loack1");
					synchronized(MyLock.lock2)
					{
						System.out.println(Thread.currentThread().getName()+":if loack2");
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lock2)
				{
					System.out.println(Thread.currentThread().getName()+":else loack2");
					synchronized(MyLock.lock1)
					{
						System.out.println(Thread.currentThread().getName()+":else loack1");
					}
				}
			}
		}
	}
}
//专门用于封装两个锁的类,并通过将锁对象定义为静态,方便通过类名访问
class MyLock
{
	staticObject lock1 = new Object();
	staticObject lock2 = new Object();
}
class DeadLock2
{
	public static void main(String[] args)
	{
		//线程执行if代码块
		Thread t1 = new Thread(new Test(true));
		//线程2执行else代码块
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值