0

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

关注我的 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;
                }
            }

我的可运行对象:

            /**
             * 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();
                    }
                }
            }
4

3 回答 3

4

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

你永远不应该调用一个Runnable对象的run()方法(除非你真的想在调用线程中运行代码)。有关更多信息,请参阅本教程

于 2012-12-04T04:04:06.740 回答
1

只有当有两个资源可以被“锁定”以进行独占访问时才会发生死锁(我将它们称为“锁”,尽管它们可能是任何这样的资源),使用模式如下:

  • 然后进程A打算获得锁XY
  • 然后进程B打算获得锁YX

如果 processA获得 lockX并且 processB获得 lock Y,您将遇到死锁。

这个版本是你(必须)在这里进行的。

于 2012-12-04T04:03:59.660 回答
0

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

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

  1. 检测死锁并“打破”它们。
  2. 使用必须按给定顺序获取锁的“优先级”方案。

检测死锁可以在简单的情况下使用死锁跟踪机制来完成,其中,当线程等待锁时,它会检查它正在等待的线程持有的锁,以查看是否有任何(直接或间接)导致返回当前线程。

死锁也可以通过超时机制来检测,尽管这种方案通常无法区分死锁和长时间运行的操作。

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

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

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

于 2012-12-04T04:26:09.350 回答