【问题标题】:Understanding Lock class vs synchronization了解锁类与同步
【发布时间】:2016-04-17 11:33:46
【问题描述】:

在 Java 并发实践中,我提出了避免死锁的技术。当我们需要获取多个锁以保证一致性时,他提出获取使用tryLock()。如下:

public void m(MyObject o1, MyObject o2){
    synchronized(o1){
        synchornized(o2){
            //...
        }
    }
}

我们最好使用这个:

public void m(MyObject o1, MyObject o2){
    while(true){
        if(o1.lock.tryLock(){
             try{
                 if(o2.lock.tryLock(){ 
                     try{
                          //...
                     } finally {
                          o2.lock.unlock();
                     }
                 }
            } finally { 
                  o2.lock.unlock()
            }
        }
    }
}

现在他走了:

此技术仅在两个锁同时获取时才有效;如果 由于嵌套方法调用而获得了多个锁,您不能 只需松开外锁,即使您知道自己握着它。

这不是很明显。为什么在方法中调用方法时不能使用它?可以举个例子吗?

【问题讨论】:

标签: java multithreading locking


【解决方案1】:

我刚刚查看了这本书,发现引用的语句是在定时锁的上下文中进行的(使用tryLock(long time, TimeUnit unit)),但我认为它也适用于您的tryLock() 示例。

从理论上讲,您仍然可以将此技术用于嵌套方法调用,但它会很快变得笨拙。让我们考虑一个简单的示例,您尝试在方法foo(Lock lock) 中获得一些锁,如果成功,则在几个堆栈帧之后您尝试在方法bar(Lock lock) 中获得另一个锁。

  • 如果你的两个方法都使用while (true)直到他们最终设法获得他们的锁,如果一个线程在foo()中获得A,另一个线程在@987654329中获得B,你很容易陷入死锁@ 和现在都在bar() 中不停地旋转,试图获取对方的锁。
  • 如果你只在foo()方法中循环并在获取锁不成功时从bar()返回,你也许可以避免死锁,但是现在:
    • foo() 和 bar() 紧密耦合
    • 取决于究竟多晚调用bar(),获取第一个锁和获取第二个锁之间的漏洞窗口可能很大
    • 你需要从foo()中的循环开始重新计算整个分支
    • 目标代码路径变得非常难以遵循且容易破解
  • 如果你使用定时锁,你仍然会遇到上面的一些问题,你需要处理InterruptedException,你需要决定你想如何循环。
    • 一般的技术已经容易产生大量的IllegalMonitorStateExceptions,这不是太好

如果您经常获取相同的锁子集并使用相同的资源子集,您可能需要考虑锁粗化(将几个锁合并为一个)或重新定义资源保护策略,以便您无需在单个工作流程中获得多级锁。

附:经过几次尝试,我得到了tryLock() 和定时tryLock(long time, TimeUnit unit) 方法来使用上述双方法双锁方案。不漂亮,我可以很容易地看到受保护部分的简单更改会如何破坏整个方案或使其过于复杂。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多