java多线程避免死锁的方法

当两个线程同时运行时,A线程拥有lock A,在等待lock B,而B线程拥有lock B,在等待lock A,那么这两个线程都在等待对方释放锁,并且谁也不会先释放锁,那么就陷入了死锁。

死锁的解决办法有三个,设置加锁顺序、设置加锁时限、开启死锁检测。


加锁顺序

当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。线程只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁,并对这些锁做适当的排序,有些时候这是无法预知的。


加锁时限

在尝试获取锁的时候加一个超时时间,也就是在尝试获取锁的过程中如果超过了这个时限该线程则放弃对该锁请求。如果一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行。
不过由于存在线程执行的超时,所以不能认为超时了就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

超时和重试机制是为了避免在同一时间出现的竞争,但是当线程很多时,其中两个或多个线程的超时时间一样或者接近的可能性就会很大,因此就算出现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。


扫描二维码关注公众号,回复: 884468 查看本文章

死锁检测

每当一个线程获得了锁,会在线程和锁相关的数据结构中(例如map)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。
当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁。
发生了死锁一种办法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁。
一个更好的办法是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

猜你喜欢

转载自blog.csdn.net/wanderlustlee/article/details/79660529