【问题标题】:This simple code should generated a deadlock but it doesn't这个简单的代码应该会产生死锁,但它不会
【发布时间】:2011-10-20 11:43:18
【问题描述】:

类似于问题I posted yesterday,我有这个问题,我就是无法理解。代码非常简单,应该(我认为)产生死锁。我什至将账户数量减少到 2 个,以增加死锁的概率。

代码真的很容易理解,但要加上一些上下文。我有一家有账户的银行,我在账户之间进行了大量转账。转移方法应该会产生死锁。为什么没有发生这种情况?

我只能认为代码运行得太快了,但这似乎不太可能一直发生

这是整个代码: http://pastebin.com/HWJpuT38

【问题讨论】:

  • 为什么你的代码会产生死锁?
  • 最简单的解决方案是对 Bank 对象进行全局锁定。由于与您要保护的操作相比,锁定相对昂贵,因此这可能会更快。
  • 尝试在您的同步语句之间放置一个线程睡眠以尝试强制解决问题。 "synchronized(fromAccount) { Thread.sleep(100); synchronized(toAccount) {"
  • 想象 2 次传输,从 1 到 2 和 2 到 1。可能会发生第一个线程在 1 上获得锁,然后第二个线程在 2 上获得锁。那么它们都将是等待对方释放锁,不会发生,死锁。

标签: java concurrency synchronized


【解决方案1】:

问题出在这一行:

mAccounts = new ArrayList<Account>(Collections.nCopies(slots, new Account()));

基本上,只有一个Account 对象,但有很多对它的引用。因此,您只需要锁定一个对象。

如果您创建许多不同的Account 对象,您应该能够很快看到死锁。

【讨论】:

  • 一般来说,如果我想用“空”对象初始化一个列表,我不能使用nCopies?因为这会将相同的对象添加到列表中的每个元素,而不是为每个元素创建一个新对象,对吗?我知道这不是同一个问题的一部分,但是是否有类似的方法来初始化空对象列表以替代 foreach 循环?
  • 不,nCopies 只会复制引用,不会复制对象。最好和最清晰的方法就是使用循环。
【解决方案2】:

您拥有“竞争”资源的唯一地方是您在fromaccount 上同步,然后在toaccount 上同步 - 其他一切仅取决于一个锁。

如果您有另一个方法在 toaccount 上同步,然后在 fromaccount 上同步,您可能会导致死锁,但就目前的代码而言,它应该表现得非常好。

【讨论】:

    【解决方案3】:

    我认为您需要在AccountTransferRunnable 中为循环添加某种睡眠,否则调度程序将运行线程直到结束,然后再启动另一个。

    使用睡眠,您将让调度程序有机会切换到另一个线程,如果第一个线程仍在运行,这将使您的代码有机会陷入死锁。

    【讨论】:

    • 如果没有睡眠,我怀疑会有任何真正的并发,因为第一个线程将在 cpu 上运行,直到它完成所有迭代,然后第二个线程得到它的 cpu 时间。在现实世界的应用程序中,你总是有某种睡眠(例如外部 I/O)
    • 如果是这种情况,并且如果我在for 循环中添加了println(打印正在运行的线程号),我应该期待大量打印“线程1”并且仅在之后该线程完成我会开始看到“线程2”。正确的?但这不是这里的情况,印刷品都是混合的。我是否以错误的方式看待并发?
    • 我认为这取决于测试环境,我已经遇到过这个问题,在单台 cpu 机器(测试用例)上忘记了睡眠,所以没有真正的并发。但是正如我看到的那样,其他人已经发现了您代码的真正问题;)顺便说一句:println 也会改变计时行为。
    【解决方案4】:
    mAccounts = new ArrayList<Account>(Collections.nCopies(slots, new Account()));
    

    http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#nCopies%28int,%20java.lang.Object%29

    您最终会得到一个包含 2 个对同一对象的引用的列表。

    该对象一次只能被一个线程锁定。你永远不会陷入僵局。

    我假设您想使用 Account 类的 2 个不同实例来初始化 mAccounts。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-10
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 2017-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多