Java 多线程编程之七:死锁(附源代码)

Java 多线程编程之七:死锁(附源代码)


例子 一:


在实际编程中,要尽量避免出现死锁的情况,但是让你故意写一个死锁的程序时似乎也不太简单(有公司会出这样的面试题),以下是一个简单的死锁例子,程序说明都写着类的注释里了,有点罗嗦,但是应该也还是表述清楚了的。


/**
* 一个简单的死锁类
* @author iStar
* 当类的对象flag=1时(T1),先锁定O1,睡眠500毫秒,然后锁定O2;
* 而T1在睡眠的时候另一个flag=0的对象(T2)线程启动,先锁定O2,睡眠500毫秒,等待T1释放O1;
* T1睡眠结束后需要锁定O2才能继续执行,而此时O2已被T2锁定;
* T2睡眠结束后需要锁定O1才能继续执行,而此时O1已被T1锁定;
* T1、T2相互等待,都需要对方锁定的资源才能继续执行,从而死锁。

class DeadLock implements Runnable {
public int flag = 1;
static Object o1 = new Object(), o2 = new Object();

@Override
public void run() {
System.out.println("flag=" + flag);
if(flag == 1) {
synchronized(o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(o2) {
System.out.println("1");
}
}
}
if(flag == 0) {
synchronized(o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized(o1) {
System.out.println("0");
}
}
}
}

public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
new Thread(td1).start();
new Thread(td2).start();

}
}


例子二:


源代码下载
        多线程编程中,线程死锁也是一个比较有趣的问题。然而死锁发生的可能性很小,正因如此,大家可能对此不是很熟悉。但是死锁并不是不重要,因为它确确实实存在着,随时会出现在我们的程序之中。很多朋友面试的时候都遇到过这样类似的一个编程题:使用 Java 写一个死锁。问题不是很难,但是由于大家现实中处理的比较少,还真难住了一部分人。本文列举了一个简单但又不失为经典的死锁的源代码,并解释了死锁发生的原因。相信读者看过之后对死锁会有更进一步的认识!
        死锁发生的原因一般是两个对象的锁相互等待造成的。比如线程 1 先拿到了对象 A 的对象锁,线程 2 先拿到了对象 B 的对象锁。线程 1 这时候要求拿到对象 B 的对象锁,因为对象 B 的对象锁暂时被线程 2 所持有,所以线程 1 只能抱着对象 A 的对象锁等待线程 2 释放掉对象 B 的对象锁。而如果这个时候,持有对象 B 的对象锁的线程 2 要求访问对象 A 的对象锁,因为对象 A 的对象锁暂时被线程 1 所持有,所以线程 2 只能抱着对象 B 的对象锁等待线程 1 释放掉对象 A 的对象锁。这个时候两个线程就陷入了互相等待的僵局之中。程序就进入了死锁的状态,除非你手工停掉这个程序,否则它将永久地僵持下去。
        是不是有点绕?嘿嘿,如果你弄明白了对象锁的概念,死锁的原因也就一目了然。关于对象锁,作者在上一篇博客《Java 多线程编程之六:线程之间的通信(附源代码)》中有所介绍,这里就不再赘述了。
        用代码说明问题,直接看代码演示。

死锁例子-资源源代码

package com.defonds.deadlock;
/**
 * 
 * 
 * 项目名称:ThreadDeadLock
 * 类名称:Resource
 * 类描述:资源类,用于代表线程竞争的数据资源
 * 创建人:Defonds
 * 创建时间:2010-1-26 下午02:01:16
 * 修改人:Defonds
 * 修改时间:2010-1-26 下午02:01:16
 * 修改备注:
 * @version 
 *
 */
public class Resource {
	
	private int value;//资源的属性
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}
}


死锁例子-资源管理器源代码:

package com.defonds.deadlock;
/**
 * 
 * 
 * 项目名称:ThreadDeadLock
 * 类名称:ResourceManager
 * 类描述:资源管理类,对资源数据操作的接口
 * 创建人:Defonds
 * 创建时间:2010-1-26 下午02:04:59
 * 修改人:Defonds
 * 修改时间:2010-1-26 下午02:04:59
 * 修改备注:
 * @version 
 *
 */
public class ResourceManager {
	
	/**
	 * 管理的两个资源
	 */
	private Resource resourceA = new Resource();
	private Resource resourceB = new Resource();
	
	/**
	 * 创建一个新的实例 ResourceManager.
	 */
	public ResourceManager(){
		this.resourceA.setValue(0);
		this.resourceB.setValue(0);
	}
	
	/**
	 * 资源的读取
	 */
	public int read(){
		synchronized(this.resourceA){
			System.out.println(Thread.currentThread().getName() + "线程拿到了资源 resourceA 的对象锁");
			synchronized(resourceB){
				System.out.println(Thread.currentThread().getName() + "线程拿到了资源 resourceB 的对象锁");
				return this.resourceA.getValue() + this.resourceB.getValue();
			}
		}
	}
	
	/**
	 * 资源的改写
	 */
	public void write(int a,int b){
		synchronized(this.resourceB){
			System.out.println(Thread.currentThread().getName() + "线程拿到了资源 resourceB 的对象锁");
			synchronized(this.resourceA){
				System.out.println(Thread.currentThread().getName() + "线程拿到了资源 resourceA 的对象锁");
				this.resourceA.setValue(a);
				this.resourceB.setValue(b);
			}
		}
	}
}



死锁例子-自定义线程源代码:

package com.defonds.deadlock;
/**
 * 
 * 
 * 项目名称:ThreadDeadLock
 * 类名称:CustomizeThread
 * 类描述:自定义线程类
 * 创建人:Defonds
 * 创建时间:2010-1-26 下午02:29:01
 * 修改人:Defonds
 * 修改时间:2010-1-26 下午02:29:01
 * 修改备注:
 * @version 
 *
 */
public class CustomizeThread extends Thread {
	private ResourceManager resourceManger;//资源管理类的私有引用,通过此引用可以通过其相关接口对资源进行读写
	private int a,b;//将要写入资源的数据
	
	/**
	 * 
	 * 创建一个新的实例 CustomizeThread.
	 * @param resourceManger
	 * @param a
	 * @param b
	 */
	public CustomizeThread(ResourceManager resourceManger,int a,int b){
		this.resourceManger = resourceManger;
		this.a = a;
		this.b = b;
	}
	
	/**
	 * 重写 java.lang.Thread 的 run 方法
	 */
	public void run(){
		/**
		 * 为了演示死锁的出现,这里对资源进行反复读写
		 * 实际业务中可能只读写一次
		 */
		while(true){
			this.resourceManger.read();
			this.resourceManger.write(this.a, this.b);
		}
	}
}


死锁例子-程序入口源代码:


package com.defonds.deadlock;
/**
 * 
 * 
 * 项目名称:ThreadDeadLock
 * 类名称:DeadLockApp
 * 类描述:死锁程序入口
 * 创建人:Defonds
 * 创建时间:2010-1-26 下午02:37:27
 * 修改人:Defonds
 * 修改时间:2010-1-26 下午02:37:27
 * 修改备注:
 * @version 
 *
 */
public class DeadLockApp {
	public static void main(String[] args) {
		
		/**
		 * 死锁演示线程初始化
		 */
		ResourceManager resourceManager = new ResourceManager();
		CustomizeThread customizedThread0 = new CustomizeThread(resourceManager,1,2);
		CustomizeThread customizedThread1 = new CustomizeThread(resourceManager,2,4);
		
		/**
		 * 死锁演示线程启动
		 */
		customizedThread0.start();
		customizedThread1.start();
	}
}


下面一次执行本代码时死锁发生的控制台输出片段:
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceA 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-0线程拿到了资源 resourceB 的对象锁
Thread-1线程拿到了资源 resourceA 的对象锁

        由上述例子可以看出,一旦两个线程互相等待的局面出现,死锁现象就不可避免地发生了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值