当两个条件对象分配给锁定对象时死锁

时间:2012-12-04 03:59:38

标签: java multithreading locking deadlock conditional-statements

出于某种原因,当我将两个条件对象分配给我的锁定对象时,我的程序死锁。当我注释掉其中一个条件对象时,它不会死锁。在将多个条件对象分配给单个锁定对象时,是否有一些我缺少的东西?以下是我的全部代码,以防您希望完整地查看它。非常感谢你提前给予的帮助和时间!

关注我的BankAccount类,其中包含锁定和条件对象作为实例字段:

            import java.util.concurrent.locks.Condition;
            import java.util.concurrent.locks.Lock;
            import java.util.concurrent.locks.ReentrantLock;

            public class BankAccount
            {
                public static final double MAX_BALANCE = 100000;

                private double balance;
                private Lock balanceChangeLock;
                private Condition sufficientFundsCondition; // signals that funds > 0 to allow withdrawal
                private Condition lessThanMaxBalanceCondition; // signals that balance < 100000 to allow more deposits

                /**
                 * Constructs a bank account with a zero balance
                 */
                public BankAccount()
                {
                    balance = 0;
                    balanceChangeLock = new ReentrantLock();
                    sufficientFundsCondition = balanceChangeLock.newCondition();
                    lessThanMaxBalanceCondition = balanceChangeLock.newCondition();
                }

                /**
                 * deposits money into the bank account
                 * @param amount the amount to deposit
                 * @throws InterruptedException
                 */
                public void deposit(double amount) throws InterruptedException
                {
                    balanceChangeLock.lock();
                    try
                    {
                        while(balance + amount > MAX_BALANCE)
                            lessThanMaxBalanceCondition.await();
                        System.out.print("Depositing " + amount);
                        double newBalance = balance + amount;
                        System.out.println(", new balance is " + newBalance);
                        balance = newBalance;
                        sufficientFundsCondition.signalAll();
                    }
                    finally
                    {
                        balanceChangeLock.unlock();
                    }
                }

                /**
                 * withdraws money from the bank account
                 * @param amount the amount to withdraw
                 * @throws InterruptedException
                 */
                public void withdraw(double amount) throws InterruptedException
                {
                    balanceChangeLock.lock();
                    try
                    {
                        while (balance < amount)
                            sufficientFundsCondition.await();
                        System.out.print("Withdrawing " + amount);
                        double newBalance = balance - amount;
                        System.out.println(", new balance is " + newBalance);
                        balance = newBalance;
                        lessThanMaxBalanceCondition.signalAll();
                    }
                    finally
                    {
                        balanceChangeLock.unlock();
                    }
                }

                /**
                 * gets the current balance of the bank account
                 * @return the current balance
                 */
                public double getBalance()
                {
                    return balance;
                }
            }

My Runnable对象:

            /**
             * a deposit runnable makes periodic deposits to a bank account
             */
            public class DepositRunnable implements Runnable
            {
                private static final int DELAY = 1;

                private BankAccount account;
                private double amount;
                private int count;

                /**
                 * constructs a deposit runnable
                 * @param anAccount the account into which to deposit money
                 * @param anAmount the amount to deposit in each repetition
                 * @param aCount the number of repetitions
                 */
                public DepositRunnable(BankAccount anAccount, double anAmount, int aCount)
                {
                    account = anAccount;
                    amount = anAmount;
                    count = aCount;
                }

                public void run()
                {
                    try
                    {
                        for (int i = 0; i <= count; i++)
                        {
                            account.deposit(amount);
                            Thread.sleep(DELAY);
                        }
                    }
                    catch (InterruptedException exception)
                    {
                    }
                }
            }

...

            /**
             * a withdraw runnable makes periodic withdrawals from a bank account
             */
            public class WithdrawRunnable implements Runnable
            {
                private static final int DELAY = 1;

                private BankAccount account;
                private double amount;
                private int count;

                /**
                 * constructs a withdraw runnable
                 * @param anAccount the account from which to withdraw money
                 * @param anAmount the amount to deposit  in each repetition
                 * @param aCount the number of repetitions
                 */
                public WithdrawRunnable(BankAccount anAccount, double anAmount, int aCount)
                {
                    account = anAccount;
                    amount = anAmount;
                    count = aCount;
                }

                public void run()
                {
                    try
                    {
                        for (int i = 0; i <= count; i++)
                        {
                            account.withdraw(amount);
                            Thread.sleep(DELAY);
                        }
                    }
                    catch (InterruptedException exception)
                    {
                    }
                }
            }

我的主要方法类,我构建我的Thread对象,等等:

            /**
             * this program runs threads that deposit and withdraw money from the same bank account
             */
            public class BankAccountThreadRunner
            {
                public static void main(String[] args)
                {
                    BankAccount account = new BankAccount();

                    final double AMOUNT = 10000;
                    final int REPETITIONS = 10;
                    final int DEPOSIT_THREADS = 10;
                    final int WITHDRAW_THREADS = 2;

                    for (int i = 0; i < DEPOSIT_THREADS; i++)
                    {
                        DepositRunnable deposits = 
                                new DepositRunnable(account, AMOUNT, REPETITIONS);
                        Thread depositThread = new Thread(deposits);
                        depositThread.run();
                    }

                    for (int i = 0; i < WITHDRAW_THREADS; i++)
                    {
                        WithdrawRunnable withdrawals = 
                                new WithdrawRunnable(account, AMOUNT, REPETITIONS);
                        Thread withdrawThread = new Thread(withdrawals);
                        withdrawThread.run();
                    }
                }
            }

3 个答案:

答案 0 :(得分:4)

您只使用一个帖子。您的代码在任何时候都不会启动或创建任何其他线程。你创建了Runnable个对象,但是你永远不会启动任何线程,而是从主线程中调用它们的run方法!

你永远不应该调用Runnable对象的run()方法(除非你真的想在调用线程中运行代码)。有关详细信息,请参阅this tutorial

答案 1 :(得分:1)

只有当两个资源可以被“锁定”以进行独占访问时才会出现死锁(我称之为“锁定”,尽管它们可能是任何此类资源),但是使用模式如下:

  • 流程A打算获取锁X,然后Y
  • 流程B打算获取锁Y,然后X

如果流程A获得了锁定X而流程B获得了锁定Y,那么您将陷入僵局。

这是你(必须)在这里发生的一个版本。

答案 2 :(得分:0)

事实是,无论何时允许线程一次锁定多个资源并且不采取任何预防措施(并且实际上存在多线程),死锁是可能的(并且几乎肯定会发生)最终)。

处理死锁基本上有两种选择:

  1. 检测死锁并“破坏”它们。
  2. 使用“优先级”方案,其中必须以给定顺序获取锁。
  3. 使用死锁跟踪机制可以在简单的情况下完成检测死锁,其中,当线程等待锁时,它会检查线程所持有的锁,它正在等待查看是否有任何(直接或间接)引导回到当前的主题。

    也可以使用超时机制检测死锁,但此方案通常无法区分死锁和长时间运行。

    在任何一种情况下,当检测到死锁时,一个或多个线程被迫放弃部分或全部锁定(这可能需要某种“回滚”机制来进行部分更改)。

    最简单的“优先级”方案是一次只允许一个锁。稍微复杂一点的是要求立即获取所有锁。真正的优先级方案为不同的类别分配不同的可锁定资源,然后以“优先级顺序”对类别进行“排序”,这样如果您已经在类别N或类别中保留任何锁定,则无法获取类别N上的任何锁定; Ñ

    但是,遗憾的是,任何方案(除了可能的基本超时方案)都可以通过与消息通信并等待来自其他线程的消息的线程撤消。如果你考虑一下,这在概念上与等待获得另一个线程的锁定相同。