一、死锁
死锁是指两个或多个线程被阻塞等待获取死锁中的某些其他线程所持有的锁。 当多个线程同时需要相同的锁,但是以不同的顺序获取它们时,可能会发生死锁。
实例1
Thread 1 locks A, waits for B
Thread 2 locks B, waits for A
实例2
Thread 1 locks A, waits for B
Thread 2 locks B, waits for C
Thread 3 locks C, waits for D
Thread 4 locks D, waits for A
数据库死锁
如果多个事务在需要同时更新相同记录,则可能陷入死锁。
由于锁是在不同的请求中进行的,并且并非提前知道给定事务所需的所有锁,因此很难检测或防止数据库事务中的死锁。
实例3
Transaction 1, request 1, locks record 1 for update
Transaction 2, request 1, locks record 2 for update
Transaction 1, request 2, tries to lock record 2 for update.
Transaction 2, request 2, tries to lock record 1 for update.
二、死锁预防
死锁四个必要条件:
- 互斥
- 占有且等待
- 不可剥夺
- 循环等待
只要破坏其中任意一个条件,就不会发生死锁。
1、加锁顺序(破坏循环等待)
在多个线程需要同时获取相同的锁,但是获取锁的顺序不同,则可能发生死锁。所以用相同的加锁顺序,可以预防死锁。但是需要提前知道程序需要获取的所有锁。
Thread 1:
lock A
lock B
Thread 2:
wait for A
lock C (when A locked)
Thread 3:
wait for A
wait for B
wait for C
2、加锁超时(破坏占有且等待)
给线程设置一个获取所有锁的超时时间,当线程无法在超时时间内获取所有锁,则释放所有锁,并等待一个随机时间,然后再重试获取锁。
如果获取相同锁的线程过多,那么每个线程的等待时间将很接近,将会不断重试获取锁,然后释放,等待,再次尝试获取。并且很难设置获取锁的超时时间。
Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.
3、死锁检测
资源占有、请求的有向图,当图无法简化成不循环的图,就会发生死锁。
三、死锁解决
- 同时释放所有的锁,等待一段随机时间后重试。但是会重新获取锁时,依旧可能导致死锁,并且浪费资源。(破坏占有且等待)
- 随机选择一个或者一些线程释放其持有的锁。也可以根据优先级,优先级高的优先获取锁。(破坏不可剥夺)