面试题答案
一键面试死锁产生的四个必要条件
- 互斥条件:资源在某一时刻只能被一个线程占用。例如,一个文件不能同时被两个线程写入。
- 占有并等待条件:一个线程持有至少一个资源,并正在等待获取其他线程持有的资源。比如线程A持有资源R1,同时等待资源R2,而R2被线程B持有。
- 不可剥夺条件:资源只能由持有它的线程主动释放,不能被其他线程强行剥夺。即线程A持有的资源,其他线程不能直接抢走。
- 循环等待条件:存在一组线程(T1, T2, ..., Tn),T1等待T2持有的资源,T2等待T3持有的资源,以此类推,Tn等待T1持有的资源,形成一个循环等待的环。
死锁示例代码
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1():
lock1.acquire()
print('Thread 1 acquired lock1')
lock2.acquire()
print('Thread 1 acquired lock2')
lock2.release()
print('Thread 1 released lock2')
lock1.release()
print('Thread 1 released lock1')
def thread2():
lock2.acquire()
print('Thread 2 acquired lock2')
lock1.acquire()
print('Thread 2 acquired lock1')
lock1.release()
print('Thread 2 released lock1')
lock2.release()
print('Thread 2 released lock2')
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()
在上述代码中,thread1
先获取lock1
,然后尝试获取lock2
,而thread2
先获取lock2
,然后尝试获取lock1
。如果thread1
获取了lock1
,thread2
获取了lock2
,就会发生死锁,因为它们相互等待对方释放资源。
避免死锁的策略及代码实现
- 破坏占有并等待条件:一次性获取所有需要的资源,而不是逐步获取。
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1():
with threading.Lock():
lock1.acquire()
lock2.acquire()
print('Thread 1 acquired both locks')
lock2.release()
lock1.release()
def thread2():
with threading.Lock():
lock1.acquire()
lock2.acquire()
print('Thread 2 acquired both locks')
lock2.release()
lock1.release()
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()
在这个代码中,使用一个全局锁来确保在获取lock1
和lock2
时不会发生占有并等待的情况。
- 破坏循环等待条件:给资源分配一个序号,线程按照序号递增的顺序获取资源。
import threading
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1():
locks = [lock1, lock2]
locks.sort(key=lambda x: id(x))
for lock in locks:
lock.acquire()
print('Thread 1 acquired locks in order')
for lock in reversed(locks):
lock.release()
def thread2():
locks = [lock2, lock1]
locks.sort(key=lambda x: id(x))
for lock in locks:
lock.acquire()
print('Thread 2 acquired locks in order')
for lock in reversed(locks):
lock.release()
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
t1.join()
t2.join()
这里通过对锁按照id
排序,确保所有线程都按照相同顺序获取锁,从而避免循环等待。