【问题标题】:Compare and Swap Incorrect value比较和交换不正确的值
【发布时间】:2013-10-05 22:50:43
【问题描述】:

我有以下代码用于锁定具有特定用户 ID 的对象

 public boolean acquireLock(Long id) {
   if (lock.compareAndSet(0L, id)) { 
    return true ; 
  }
  return false ; 
}

我通过以下方式获取它:

      while(!parent.acquireLock(id)){
        System.out.println(lock.get());
        if (count++>1000000) {
          System.out.println(id + " Trying to acquire " + lock.get());
          DebugHandler.createException("Error, deadlock");

        }
      }

发布为:

public boolean releaseLock(Long id) { 

  if (lock.compareAndSet(id, 0)) {
    System.out.println("Releasing Lock for " + id);
    return true ; 
  }
  else { 
    DebugHandler.createException("Lock not owned by current view. Thief");
    return false ;
  }

}

并将锁对象声明为:

 private volatile AtomicLong lo = new AtomicLong(0); 

除了我得到以下奇怪的行为和死锁,它的结尾是:

Id 45 试图获取 0

好吧,锁的值系统地为 0,但比较和交换测试失败,认为它不是 0。(用于测试死锁的计数器在我退出循环后重新初始化)

有什么想法吗?

【问题讨论】:

  • 在您的代码中,您在获取锁时使用this.getWorldID(),但您的日志记录使用this.id。请解决此问题并重新测试,让我们知道结果如何。
  • 真的总是0吗?您可以期望它有时为 0。这并不意味着死锁,只是自旋锁被另一个线程持有。 AtomicLong 是原子的,但它不是阻塞。
  • 你记得在成功时将count 设置为 0 吗?
  • 是的,它始终为 0,至少对于 1000000 次迭代。而且我确实重置了计数。
  • 您正在呼叫parent.acquireLock(),但正在打印lock.get()。那些是 same 锁吗? parent.lock 看起来更像你要找的锁。

标签: java concurrency locking atomic


【解决方案1】:

我发现您的代码有问题。如果你得到某个id 为零的线程,你的锁定方案就会崩溃,两个线程最终可能同时持有锁。

修复:在您的 acquireLockreleaseLock 方法中添加防御性检查,以测试 id 是否非零。

关于您的代码如何进入明显的死锁状态:

  • 这可能真的是一个真正的死锁。例如,如果线程尝试获取给定id 的锁,而它已经持有该id 的锁,就会发生这种情况。 (您实现的锁是不可重入的。)

  • 您的代码可能未能释放给定id 的锁定;例如因为抛出了一个异常并且跳过了对releaseLock的正常调用。

【讨论】:

  • Id 不是 0,因为它们从 1 开始分配。调试输出显示一个非 0 id 线程正在尝试获取值为 0 的锁,但 CaS 测试仍然失败。
  • 1) 无论如何,您都应该对0 进行防御性检查。 2) CaS 测试失败的原因是锁已经被其他东西持有。我给了你两种可能的解释。可能还有其他人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 2011-05-11
  • 2013-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多