【校招VIP】死锁发生的原因和避免方法

1天前 收藏 0 评论 0 java开发

【校招VIP】死锁发生的原因和避免方法

转载声明:文章来源https://blog.csdn.net/m0_57836225/article/details/143222512

一、死锁是什么
死锁是指两个或多个进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。例如,进程 P1 持有资源 R1 并等待获取资源 R2,而进程 P2 持有资源 R2 并等待获取资源 R1,此时 P1 和 P2 就陷入了死锁状态。

二、死锁发生的原因
(一)资源竞争
1)互斥资源:系统中的某些资源一次只能被一个进程(或线程)使用,如打印机、磁带机等。当多个进程(或线程)都需要使用这种互斥资源,且它们同时请求该资源时,就可能导致死锁。
2)不可抢占资源:一旦某个进程(或线程)获得了某些不可抢占资源,在它使用完之前,不能被其他进程(或线程)强行夺走。例如,进程已经获得了内存空间,在它释放之前,其他进程无法抢占该内存。如果多个进程都持有不可抢占资源且都在等待其他进程释放资源,就可能引发死锁。
(二)进程间通信
1)进程同步错误:当多个进程(或线程)通过信号量、锁等机制进行同步时,如果同步操作的顺序不当或者信号量的设置不合理,就可能导致死锁。例如,进程 P1 先等待进程 P2 发送信号,而进程 P2 又在等待进程 P1 发送另一个信号,这样就形成了死锁。
2)循环等待:在一个进程集合中,每个进程都在等待其他进程所占用的资源,而形成一种循环等待的局面。例如,进程 P1 等待进程 P2 占用的资源 R2,进程 P2 等待进程 P3 占用的资源 R3,进程 P3 又等待进程 P1 占用的资源 R1,这种循环等待关系很容易导致死锁。

三、避免死锁的方法
(一)预防死锁
1)破坏互斥条件:如果允许资源同时被多个进程(或线程)访问,就可以避免因互斥资源导致的死锁。但在很多实际场景中,资源的互斥性是其本身的特性,很难完全破坏。例如,打印机不能同时被多个进程打印,不过对于一些可以通过分割资源来实现部分非互斥性操作的情况,可以考虑这种方法。比如将一个大文件分成多个小文件块,不同进程可以同时读取不同的文件块。
2)破坏不可抢占条件:允许进程强行抢占其他进程已占有的资源,但这种方式需要谨慎使用,因为可能会导致系统的不稳定和数据的不一致。通常需要在抢占资源时进行一些额外的处理,如保存被抢占进程的状态,以便在合适的时候恢复其运行。
3)破坏循环等待条件:可以通过对资源进行编号,规定每个进程必须按照资源编号递增的顺序请求资源,这样就可以避免循环等待。例如,系统中有资源 R1、R2、R3,进程在请求资源时,必须先请求 R1,再请求 R2,最后请求 R3。如果已经持有了 R2,那么在请求 R3 之前不能再请求 R1。

(二)避免死锁
1)银行家算法:这是一种最著名的死锁避免算法。它通过模拟银行系统中资金的分配和回收来判断系统是否处于安全状态。在系统运行过程中,每当有进程请求资源时,系统会先检查当前系统状态是否安全(即是否存在一种资源分配方案,使得所有进程都能完成其任务)。如果系统处于安全状态,则分配资源给该进程;否则,让该进程等待。

以下是一个简单的用 Python 实现的银行家算法示例(这里只是一个简化的示意代码,实际应用中需要更完善的处理):

# 定义资源数量和进程数量
num_resources = 3
num_processes = 5

# 初始化已分配资源矩阵
allocated = [[0, 1, 0], [2, 0, 0], [3, 0, 2], [2, 1, 1], [0, 0, 2]]

# 初始化最大需求矩阵
max_demand = [[7, 5, 3], [3, 2, 2], [9, 0, 2], [2, 2, 2], [4, 3, 3]]

# 计算可用资源向量
available = [3, 3, 2] - sum(allocated, [])

# 定义函数来检查系统是否处于安全状态
def is_safe(allocated, max_demand, available):
work = available.copy()
finish = [False] * num_processes
while True:
found = False
for i in range(num_processes):
if not finish[i] and all(work[j] >= max_demand[i][j] - allocated[i][j] for j in range(num_resources)):
work = [work[j] + allocated[i][j] for j in range(num_resources)]
finish[i] = True
found = True
if not found:
break
return all(finish)

# 检查系统是否安全
if is_safe(allocated, max_demand, available):
print("系统处于安全状态")
else:
print("系统处于不安全状态")

(三)检测和解除死锁
1)死锁检测:系统可以定期或在资源请求时进行死锁检测。常用的死锁检测算法有资源分配图算法等。通过构建资源分配图,检查图中是否存在环路来判断是否发生死锁。如果存在环路,则表示系统可能处于死锁状态。
2)死锁解除:一旦检测到死锁,系统可以采取一些措施来解除死锁。常见的方法有:
剥夺资源:从一些进程中强行剥夺它们已占有的资源,分配给其他处于阻塞状态的进程,以打破死锁环路。选择被剥夺资源的进程时,可以根据进程的优先级、已运行时间等因素来决定。
终止进程:直接终止一些进程来解除死锁。可以选择终止优先级低的进程、占用资源多的进程或者是处于阻塞状态时间最长的进程等。

在实际的软件开发中,尤其是在多线程和分布式系统开发中,我们需要综合运用这些方法来避免和处理死锁问题。例如,在前端 Vue3 + TypeScript 开发中,当涉及到多个异步操作之间的资源竞争时,要合理规划和管理状态,避免出现循环等待的情况。可以通过使用 Vue 的异步函数和钩子函数,正确处理数据的加载和更新顺序。在后端 Java 开发中,对于数据库资源的访问、共享对象的使用等,要注意使用合适的同步机制,如使用 synchronized 关键字或 Lock 接口来保证资源的正确访问,同时要考虑资源的分配和释放顺序,避免死锁的发生。

总之,了解死锁发生的原因并采取有效的避免措施,是提高软件系统可靠性和性能的重要环节。通过合理的设计和编码实践,可以减少死锁带来的风险,确保系统的稳定运行。

C 0条回复 评论

帖子还没人回复快来抢沙发