当从多个线程以相反顺序执行equals()时,Java的同步集合出现问题
示例场景:
- 创建两个同步集(s1和s2)
- 将它们传递给两个线程(T1和T2)
- 启动线程
T1的run(): 而(永远) s1。等于(s2)
T2的run() 而(永远) s2。等于(s1)
会发生什么? -SynchronizedSet的equals获得自身的锁
它计算传入的参数的长度以及它包含的内容,以确定其是否相等[注意:这是基于我分析的日志的猜测]
如果传入的参数也是SynchronizedSet,那么对size()和containAll()的调用也意味着必须获取该参数的锁
在上例中,T1和T2的锁获取顺序如下:
T1:s1->;s2 T2:s2->;s1
Ofc,这会导致僵局
此问题并非仅限于同步集合。即使使用哈希表或向量,也可能发生这种情况
我认为这是一个JavaAPI限制(设计)。如何克服这个问题?如何确保我的应用程序中不会发生这种情况?在这种情况下,我是否应该遵循一些设计原则
# 1 楼答案
我相信你错了。我曾经使用过的每个PL级锁定方案的一个基本要求是,线程必须以相同的顺序锁定资源,否则就有死锁的风险。这也适用于数据库
事实上,我认为你能避免这种情况的唯一方法是:
这两种方法都是不切实际和不可扩展的
编写应用程序代码,以便所有线程以相同的顺序获取锁@莫里斯和@Nettogrof的答案给出了如何做到这一点的例子,尽管如果你有很多场景需要担心,这可能会更加困难
# 2 楼答案
可以在每个线程中以相同的顺序锁定两个集合:
及
# 3 楼答案
斯蒂芬·C的是好东西。进一步信息:如果您不知道这两个集合是哪一种方式,可以在比较这两个集合时使用“全局”锁:
如果这些集合可能会被争用,您可以在大多数情况下按标识哈希代码排序,并返回到全局锁:
有时,您可以使用另一种算法。IIRC,
StringBuffer
可以通过append死锁(尽管对象上的操作组合没有多大意义)。可以通过以下方式实施:最好的解决方案可能是改变线程策略,这样就不需要在这里进行任何锁定
# 4 楼答案
我可能建议使用synchronized(){]块
比如:
及
# 5 楼答案
在执行} 排序。这样,锁获取的顺序将始终相同
equals()
调用之前,可以按集合的^{