Multithreading Java线程活动锁

Multithreading Java线程活动锁,multithreading,livelock,Multithreading,Livelock,我有一个与Java线程活动锁相关的有趣问题。来了 有四个全局锁-L1、L2、L3、L4 有四个线程-T1、T2、T3、T4 T1需要锁L1、L2、L3 T2需要锁L2 T3所需锁L3、L4 T4需要锁L1、L2 因此,问题的模式是——任何线程都可以以任何顺序运行和获取锁。如果任何一个线程检测到它需要的锁不可用,它将释放它以前获得的所有其他锁,并在重试之前等待一段固定的时间。循环重复,导致活锁状态 所以,要解决这个问题,我有两个解决方案 1) 让每个线程在重试之前随机等待一段时间 OR, 2)

我有一个与Java线程活动锁相关的有趣问题。来了

有四个全局锁-L1、L2、L3、L4

有四个线程-T1、T2、T3、T4

T1需要锁L1、L2、L3 T2需要锁L2 T3所需锁L3、L4 T4需要锁L1、L2

因此,问题的模式是——任何线程都可以以任何顺序运行和获取锁。如果任何一个线程检测到它需要的锁不可用,它将释放它以前获得的所有其他锁,并在重试之前等待一段固定的时间。循环重复,导致活锁状态

所以,要解决这个问题,我有两个解决方案

1) 让每个线程在重试之前随机等待一段时间

OR,
2) 让每个线程按特定顺序获取所有锁(即使线程不需要所有锁) 锁)


我不相信这是我仅有的两个选择。请告知。

除非有充分理由(性能方面)不这样做, 我会将所有锁统一到一个锁对象。 这与您建议的解决方案2类似,但我认为更简单

顺便说一句,这个解决方案不仅更简单,而且更少出现bug,
性能可能比您建议的解决方案1更好。

就个人而言,我从未听说过选项1,但我绝对不是多线程方面的专家。仔细考虑后,听起来会很好用

但是,处理线程和资源锁定的标准方法与选项2有些关联。为了防止死锁,需要始终以相同的顺序获取资源。例如,如果您总是以相同的顺序锁定资源,则不会出现任何问题。

使用2a)让每个线程以特定的顺序获取它需要的所有锁(而不是所有锁);如果线程遇到一个不可用的锁,那么它将释放所有锁

只要线程以相同的顺序获得锁,就不会出现死锁;但是,您仍然可能会感到饥饿(线程可能会遇到这样的情况,即它不断释放其所有锁而不向前推进)。为了确保取得进展,您可以为线程分配优先级(0=最低优先级,MAX_INT=最高优先级)-在必须释放锁时增加线程的优先级,在获取所有锁时将其降低为0。将等待的线程放入队列中,如果低优先级线程需要与高优先级线程相同的资源,则不要启动低优先级线程-这样可以保证高优先级线程最终将获得其所有锁。但是,除非您确实遇到线程饥饿问题,否则不要实现这个线程队列,因为它可能比让所有线程同时运行效率低


您还可以通过实现omer schleifer的将所有锁压缩到一个解决方案来简化事情;但是,除非您提到的四个线程之外的其他线程正在争夺这些资源(在这种情况下,您仍然需要从外部线程锁定资源),否则您可以通过移除所有锁并将线程放入循环队列来更有效地实现这一点(因此您的线程只需保持以相同的顺序运行).

让所有线程在需要时进入一个受互斥保护的状态机,并释放它们的锁集。线程应该公开一些方法,这些方法返回它们继续运行所需的锁集,并发送信号/等待私有信号量信号。SM应该为每个锁包含一个bool和一个“等待”队列/数组/向量/列表/存储等待线程的任何容器

如果线程进入SM互斥体以获取锁,并且可以立即获取其锁集,那么它可以重置其bool集,退出互斥体并继续

如果一个线程进入SM互斥体并且不能立即获得它的锁,它应该将自己添加到“等待”,退出互斥体并等待它的私有信号量

如果一个线程进入SM互斥锁以释放其锁,它会将锁布尔设置为“返回”其锁,并迭代“等待”,试图找到一个现在可以使用可用锁集运行的线程。如果它找到了一个线程,它会适当地重置bools,从“等待”中删除它找到的线程,并向“找到”的线程信号量发出信号。然后退出互斥锁

您可以随意使用用于将可用的set lock bool与等待线程匹配的算法。也许您应该释放需要最大匹配集的线程,或者您希望“旋转”“等待”容器元素以减少饥饿。由你决定

这样的解决方案不需要轮询(其性能会消耗CPU使用和延迟),也不需要连续获取/释放多个锁

使用OO设计开发这样的方案要容易得多。发送信号/等待信号量并返回所需锁集的方法/成员函数通常可以填充在线程类继承链的某个位置。

的确如此。(1) 具有可避免的延迟,并且(2)具有极大的死锁可能性,正如Zim Zam所强调的,除非无法获得锁的线程释放已经获得的锁并稍后重试。