2013-04-22 103 views
2

我有一个与Java线程活锁有关的有趣问题。在这里。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)具有可避免的延迟并且(2)具有极端的死锁潜力,正如Zim-Zam所强调的那样,除非未能获得锁的线程释放已经获得并稍后重试的线程。 – 2013-04-22 15:41:41

回答

0

除非有很好的理由(性能明智)不这样做, 我会统一所有锁一把锁对象。 这与您建议的解决方案2类似,只是在我看来更简单。

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

0

就我个人而言,我从未听说过选项1,但我绝不是多线程专家。想到这件事后,听起来好像可以正常工作。

然而,对付线程和资源锁定的标准方法具有一定的相关选项2.为了防止死锁,资源需要总是以相同的顺序进行收购。例如,如果您始终以相同的顺序锁定资源,则不会有任何问题。

0

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

只要线程获得他们在你不能有死锁相同的顺序锁;然而,你仍然可能会饿死(一个线程可能会遇到一个情况,它会一直释放所有的锁而不会前进)。为了确保取得进展,可以为线程分配优先级(0 =最低优先级,MAX_INT =最高优先级) - 在释放锁时增加线程的优先级,并在获取所有锁时将其减少到0。把你的等待线程放入一个队列中,如果它需要与更高优先级的线程相同的资源,则不要启动低优先级的线程 - 这样可以保证优先级更高的线程最终获得所有的锁。不要实现这个线程队列,除非你实际上遇到线程匮乏的问题,因为它可能比只让所有线程同时运行效率低。

您还可以通过实施omer schleifer的浓缩 - 所有锁定解决方案来简化操作;然而,除非你提到的四个线程之外的线程正在争夺这些资源(在这种情况下,你仍然需要锁定外部线程中的资源),你可以通过移除所有锁并将线程在一个循环队列中(所以你的线程只是按照相同的顺序运行)。

1

当所有线程需要并释放它们的一组锁时,它们都进入单个受互斥锁保护的状态机。线程应该公开一些方法,它们返回它们需要继续执行的一组锁,并发出/等待一个私有信号量信号。 SM应该包含每个锁的bool和一个'等待'队列/数组/矢量/列表/任何容器来存储等待线程。

如果线程进入SM互斥锁以获取锁并可立即获取其锁定集,它可以重置其布尔集,退出互斥锁并继续。

如果一个线程进入SM互斥体并且无法立即获得其锁定集,它应该将自己添加到'等待'中,退出互斥体并等待其私有信号量。

如果一个线程进入SM互斥体释放它的锁,它会设置锁布尔'返回'它的锁并迭代'等待'以试图找到一个线程,该线程现在可以使用一组可用的锁来运行。如果它找到一个,它会正确地重置bools,从'Waiting'中删除找到的线程并发出'找到'线程信号量的信号。然后它退出互斥体。

你可以用你用来匹配可用的set lock bools和你想要的等待线程的算法。也许你应该释放需要最大匹配的线程,或者你想'旋转''Waiting'容器元素以减少饥饿。由你决定。

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

用OO设计开发这样的方案要容易得多。信号/等待信号量并返回所需的一组锁的方法/成员函数通常可以填充到线程类继承链中的某处。