IT序号网

5. 线程死锁

sanshao 2022年05月02日 编程语言 253 0

1.线程上下文切换

缘由:cpu个数小于线程个数,为了让用户觉得所有程序都在并行进行,需要cpu切换时间片。线程A时间片结束后,为了下一次再次运行线程A时知道该从哪里继续运行,需要保存线程A的上下文信息。

线程切换时机:线程的cpu时间片使用完毕时,线程被其他线程中断时。

2.线程死锁

从表现定义死锁:两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象。在无外力干涉情况下,这种情况会持续等待下去。

2.1 死锁产生的四个条件

互斥条件:某资源同一时刻只能被一个线程占用。

请求并持有条件:线程持有至少一个资源,但又请求新的正在被其他线程使用的资源。

不可剥夺条件:线程再使用完毕资源之前不可被其他线程剥夺。

环路等待条件:存在线程、资源的环形链。线程集合{T0,T1,...,Tn},线程T0等待T1资源...线程Tn等待T0资源。

线程死锁代码实例

threadA开启后占用resourceA,睡眠5s,threadB开启后占用resourceB,1s后睡眠结束,获取resourceA失败阻塞,知道threadA释放resourceA才继续执行下去。

private static Object resourceA = new Object(); 
	private static Object resourceB = new Object(); 
	public static void main(String[] args) { 
		Thread threadA = new Thread(new Runnable() { 
			@Override 
			public void run() { 
				synchronized (resourceA) { 
					try { 
						System.out.println("线程A获得resourceA,开始等待"); 
						Thread.sleep(5000); 
					} catch (InterruptedException e) { 
						e.printStackTrace(); 
					} 
				} 
				synchronized (resourceB) { 
						System.out.println("线程A获得resourceB"); 
				} 
			} 
		}); 
		 
		Thread threadB = new Thread(new Runnable() { 
			@Override 
			public void run() { 
				synchronized (resourceB) { 
					try { 
						System.out.println("线程B获得resourceB,开始等待"); 
						Thread.sleep(1000); 
					} catch (InterruptedException e) { 
						e.printStackTrace(); 
					} 
				} 
				synchronized (resourceA) { 
					System.out.println("线程B获得resourceA"); 
				} 
			} 
		}); 
		 
		threadA.start(); 
		threadB.start(); 
	}

 输出:

线程B获得resourceB,开始等待 
线程A获得resourceA,开始等待 
线程A获得resourceB 
线程B获得resourceA

2.2 如何避免线程死锁

只需破坏线程死锁4个条件之一即可。但在实际开发中,只有请求保持、环路等待两个条件可被破坏。不可剥夺和互斥条件,是线程安全必备的。

具体方法:有序申请资源。

假如线程A和B都需要资源1,2,3,...,n,对资源排序,线程A和B只有在获取了资源n-1时才能获取资源n。也就是线程A和B同时、有序获取资源1,2,3,...,n


评论关闭
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

4.线程中断