5. 线程死锁

本文探讨了线程上下文切换的原因及线程死锁的概念,通过实例代码演示了死锁的发生过程,并提供了预防死锁的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值