将两个条件对象分配给锁定对象时出现死锁



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

专注于我的 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();
}
}
}

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

切勿调用Runnable对象的run()方法(除非您确实想在调用线程中运行代码)。有关详细信息,请参阅本教程。

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

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

如果进程A获取锁定X,进程B获取锁定Y,则会出现死锁。

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

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

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

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

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

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

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

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

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

最新更新