0

何らかの理由で、両方の条件オブジェクトをロック オブジェクトに割り当てると、プログラムがデッドロックします。条件オブジェクトの 1 つをコメントアウトすると、デッドロックになりません。複数の条件オブジェクトを 1 つのロック オブジェクトに割り当てる際に、私が見逃しているものはありますか? 全体を見たい場合に備えて、私のコード全体を以下に示します。事前にご協力いただき、ありがとうございました。

ロックおよび条件オブジェクトをインスタンス フィールドとして含む 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

使用しているスレッドは 1 つだけです。コードが追加のスレッドを開始または作成することは決してありません。オブジェクトを作成しRunnableますが、スレッドを起動することはなく、代わりrunにメイン スレッドからメソッドを呼び出します。

Runnableオブジェクトのメソッドを呼び出すべきではありませんrun()(実際に呼び出しスレッドでコードを実行したい場合を除きます)。詳細については、このチュートリアルを参照してください。

于 2012-12-04T04:04:06.740 に答える
1

デッドロックは、排他的アクセスのために「ロック」できるリソースが2 つある場合にのみ発生します (このようなリソースの場合もありますが、「ロック」と呼びます)。使用パターンは次のとおりです。

  • プロセスは次にAロックを取得しようとしていますXY
  • プロセスは次にBロックを取得しようとしていますYX

processAが lockXを取得し、 processBが lockを取得するとY、デッドロックが発生します。

これのバージョンは、ここで行っている (必要な) ものです。

于 2012-12-04T04:03:59.660 に答える
0

実際には、スレッドが一度に複数のリソースをロックすることを許可し、何の予防策も取らないと (そして実際にマルチスレッドが進行している場合)、デッドロックが発生する可能性があります (最終的にはほぼ確実に発生します)。

デッドロックを処理するには、基本的に 2 つのオプションがあります。

  1. デッドロックを検出し、それらを「中断」します。
  2. 特定の順序でロックを取得する必要がある「優先」スキームを使用します。

デッドロックの検出は、デッドロック トレース メカニズムを使用する単純なケースで実現できます。このメカニズムでは、スレッドがロックを待機しているときに、待機しているスレッドが保持しているロックを調べて、(直接的または間接的に) スレッドに戻るロックがあるかどうかを確認します。現在のスレッド。

デッドロックはタイムアウト メカニズムで検出することもできますが、このスキームでは通常、デッドロックと実行時間の長い操作を区別できません。

いずれの場合も、デッドロックが検出されると、1 つ以上のスレッドが強制的にロックの一部またはすべてを放棄します (これには、スレッドが行った部分的な変更に対して何らかの「ロールバック」メカニズムが必要になる場合があります)。

最も単純な「優先度」スキームは、一度に 1 つのロックしか保持できないようにすることです。すべてのロックを一度に取得する必要がある場合は、少し複雑になります。真の優先度スキームは、さまざまなロック可能なリソースをさまざまなカテゴリに割り当て、カテゴリ N またはカテゴリ > N.

しかし、残念なことに、メッセージと通信し、他のスレッドからのメッセージを待機しているスレッドによって、(おそらく基本的なタイムアウト スキームを除いて) どのスキームも元に戻すことができます。考えてみれば、これは概念的には、他のスレッドが持っているロックを取得するのを待っているのと同じです。

于 2012-12-04T04:26:09.350 に答える