我有一个与Java线程实时锁相关的有趣问题。在这里。
有四个全局锁 - L1,L2,L3,L4
有四个线程 - T1,T2,T3,T4
T1需要锁L1,L2,L3 T2需要锁L2 T3需要锁L3,L4 T4需要锁L1,L2
因此,问题的模式是 - 任何线程都可以以任何顺序运行并获取锁。如果任何线程检测到它所需的锁不可用,它将释放之前获取的所有其他锁等待一段固定时间,然后再次重试。循环重复,产生实时锁定状态。
所以,为了解决这个问题,我有两个解决方案
1)让每个线程在重试之前等待一段随机的时间。
OR,
2)让每个线程以特定顺序获取所有锁(即使线程不需要全部 锁)
我不相信这些是我可用的唯一两种选择。请指教。
答案 0 :(得分:1)
让所有线程在需要时释放一组互斥锁状态机并释放它们的锁。线程应该公开返回它们需要继续的锁定集的方法,并且还要发出信号/等待私有信号量信号。 SM应包含每个锁的bool和'Waiting'队列/数组/向量/列表/用于存储等待线程的任何容器。
如果线程进入SM互斥锁以获取锁定并且可以立即设置其锁定,则可以重置其bool设置,退出互斥锁并继续。
如果某个线程进入SM互斥锁并且无法立即设置其锁定,则应将其自身添加到“等待”,退出互斥锁并等待其私有信号量。
如果线程进入SM互斥锁以释放其锁定,它会将锁定bools设置为“返回”其锁定并迭代“等待”以尝试查找现在可以使用可用锁定集运行的线程。如果找到一个,它会适当地重置bool,从'Waiting'中删除它找到的线程并发出'found'线程信号量的信号。然后它退出互斥锁。
您可以根据需要使用您用来匹配可用的set lock bools和等待线程的算法。也许您应该释放需要最大匹配项的线程,或者您可能希望“旋转”'等待'容器元素以减少饥饿。由你决定。
这样的解决方案不需要轮询,(其性能损失的CPU使用和延迟),并且不会持续获取/释放多个锁。
使用OO设计开发这样的方案要容易得多。用于发信号/等待信号量并返回所需锁定集的方法/成员函数通常可以填充在线程类继承链的某处。
答案 1 :(得分:0)
除非有充分理由(表现明智)不这样做, 我会将所有锁统一到一个锁对象。 这类似于你建议的解决方案2,在我看来只是更简单。
顺便说一下,这个解决方案不仅更简单,而且更少的错误, 性能可能比您建议的解决方案1更好。
答案 2 :(得分:0)
就个人而言,我从未听说过选项1,但我绝不是多线程专家。在考虑之后,听起来它会正常工作。
但是,处理线程和资源锁定的标准方法与选项2有些相关。为了防止死锁,需要始终以相同的顺序获取资源。例如,如果您始终以相同的顺序锁定资源,则不会出现任何问题。
答案 3 :(得分:0)
使用2a)让每个线程以特定顺序获取它所需的所有锁(不是所有锁);如果一个线程遇到一个不可用的锁,那么它会释放所有的锁
只要线程以相同的顺序获取锁,就不会出现死锁;但是,你仍然可以拥有饥饿(一个线程可能会遇到一种情况,它会不停地释放所有锁,而不会向前推进)。为确保取得进展,您可以为线程分配优先级(0 =最低优先级,MAX_INT =最高优先级) - 在必须释放其锁时增加线程的优先级,并在获取所有锁时将其降低为0。将等待的线程放入队列中,如果需要与优先级较高的线程相同的资源,则不要启动优先级较低的线程 - 这样可以保证优先级较高的线程最终获取所有锁。不要实现这个线程队列,除非你实际上遇到线程饥饿问题,因为它可能不会让你的所有线程一次运行。
您还可以通过实施omer schleifer的firse-all-locks-to-one解决方案来简化事情;但是,除非你提到的四个以外的线程争用这些资源(在这种情况下你仍然需要从外部线程锁定资源),你可以通过删除所有锁并放置线程来更有效地实现这一点。在循环队列中(所以你的线程只是以相同的顺序运行)。