Java 理解锁类与同步

Java 理解锁类与同步,java,multithreading,locking,Java,Multithreading,Locking,在Java并发实践中,我使用了避免死锁的技术。当我们需要获取多个锁以确保一致性时,他建议获取usetryLock()。详情如下: public void m(MyObject o1, MyObject o2){ synchronized(o1){ synchornized(o2){ //... } } } 相反,我们最好使用以下方法: public void m(MyObject o1, MyObject o2){

在Java并发实践中,我使用了避免死锁的技术。当我们需要获取多个锁以确保一致性时,他建议获取use
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()
            }
        }
    }
}
现在他说:

此技术仅在同时获取两个锁时有效;如果 由于方法调用的嵌套,您无法获得多个锁 只需重新打开外部锁,即使你知道你拿着它


这不太明显。在方法中调用方法时,为什么不能使用此选项?你能举个例子吗?

我刚刚翻阅了这本书,发现引用的语句是在定时锁的上下文中做出的(使用
tryLock(long time,TimeUnit)
),但我认为它也适用于你的
tryLock()
示例

理论上,您仍然可以在嵌套方法调用中使用此技术,但它很快就会变得非常笨拙。让我们考虑一个简单的例子,您尝试在方法<代码> FoO(锁锁)中获得一些锁,如果成功,后面的两个堆栈帧将尝试在方法<代码>栏(锁锁)< /C> >中获得另一个锁。
  • 如果您的两个方法都使用
    while(true)
    ,直到它们最终设法获得锁,那么如果一个线程在
    foo()
    中获得了
    a
    ,另一个线程在
    foo()
    中获得了
    B
    ,并且现在这两个线程都在
    bar()
    中无休止地旋转,则很容易陷入死锁,试图获得对方的锁
  • 如果在锁获取失败时仅在
    foo()
    方法中循环并从
    bar()
    返回,则可以避免死锁,但现在:
    • foo()
      bar()
      紧密耦合
    • 根据调用
      bar()
      的确切时间,获取第一个锁和获取第二个锁之间的漏洞窗口可能非常大
    • 您需要从
      foo()中的循环开始重新计算整个分支
    • 目标代码路径变得很难遵循,也很容易被破坏
  • 如果使用定时锁,您仍然会遇到上述一些问题,您需要处理
    InterruptedException
    ,并且需要决定如何循环。
    • 一般技术已经倾向于生成大量的
      IllegalMonitorStateException
      s,这并不太好

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


另外,经过几次尝试,我得到了
tryLock()
和timed
tryLock(长时间,时间单位)
方法来使用上述两种方法,两种锁方案。不太好看,我可以很容易地看出,在受保护部分进行简单的更改会破坏整个方案,或者使其变得过于复杂。

请查看@Boris the Spider的评论: