如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能用wait()、notify()、notifyAll()方法进行线程通信了。当使用Lock对象来保证同步时,Java提供了Condition类来协调线程间的通信。
本示范简单模拟银行帐户的存取款活动,帐户余额大于等于取款金额时允许取款;帐户余额小于1000时允许存款(这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了)。
1. 实体Account类
packagecom.clzhang.sample.thread;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.ReentrantLock;public classAccount {//锁对象
private final Lock lock = newReentrantLock();//Condition对象
private final Condition condDeposit =lock.newCondition();private final Condition condWithdraw =lock.newCondition();//为避免double类型计算的误差,balance类型设计为int的
private intbalance;public Account(intbalance) {this.balance =balance;
}public void withdraw(intdrawAmount) {//加锁
lock.lock();try{//如果帐户余额不足,则取钱方法阻塞
while (balance
condWithdraw.await();//执行取钱
balance -=drawAmount;
System.out.println(Thread.currentThread().getName()+ " 取钱:" + drawAmount + "账户余额为:"
+balance);//唤醒存款线程
condDeposit.signal();
}catch(InterruptedException ex) {
ex.printStackTrace();
}finally{
lock.unlock();
}
}//按照目前设计,帐户余额大于1000后不让存款,必须先取款后才能再存。//这与真实业务逻辑不符合,只是技术上需要才如此做的,否则存款一下子全存完就不好玩了。
public void deposit(intdepositAmount) {
lock.lock();try{//如果帐户余额大于1000,存钱方法阻塞
while (balance > 1000)
condDeposit.await();//执行存款
balance +=depositAmount;
System.out.println(Thread.currentThread().getName()+ " 存款:" + depositAmount + "账户余额为:"
+balance);//唤醒取款线程
condWithdraw.signal();
}catch(InterruptedException ex) {
ex.printStackTrace();
}finally{
lock.unlock();
}
}
}
2. 调用类(DepositDrawTest类)
packagecom.clzhang.sample.thread;class DrawThread extendsThread {//模拟用户账户
privateAccount account;//每次取钱数
private intdrawAmount;public DrawThread(String name, Account account, intdrawAmount) {super(name);this.account =account;this.drawAmount =drawAmount;
}
@Overridepublic voidrun() {for (int i = 0; i < 3; i++) {
account.withdraw(drawAmount);try{
Thread.sleep(100);
}catch(InterruptedException e) {
}
}
}
}class DepositThread extendsThread {//模拟用户账户
privateAccount account;//每次存钱数
private intdepositAmount;public DepositThread(String name, Account account, intdepositAmount) {super(name);this.account =account;this.depositAmount =depositAmount;
}
@Overridepublic voidrun() {for (int i = 0; i < 3; i++) {
account.deposit(depositAmount);try{
Thread.sleep(100);
}catch(InterruptedException e) {
}
}
}
}public classDepositDrawTest {public static voidmain(String[] args) {//创建一个账户,初始帐户余额为0
Account acct = new Account(0);//注意下面的取款与存款的balance参数值需要匹配,//否则可能造成存款过多而不让存,然后又没有人取款导致程序无法正常终止的问题。
new DrawThread("取钱者1", acct, 400).start();new DrawThread("取钱者2", acct, 600).start();new DepositThread("存款者甲", acct, 600).start();new DepositThread("存款者乙", acct, 200).start();new DepositThread("存款者丙", acct, 400).start();
}
}
输出:
存款者甲 存款:600账户余额为:600
存款者乙 存款:200账户余额为:800
存款者丙 存款:400账户余额为:1200
取钱者1 取钱:400账户余额为:800
取钱者2 取钱:600账户余额为:200
存款者乙 存款:200账户余额为:400
存款者甲 存款:600账户余额为:1000
取钱者2 取钱:600账户余额为:400
存款者丙 存款:400账户余额为:800
取钱者1 取钱:400账户余额为:400
存款者甲 存款:600账户余额为:1000
存款者丙 存款:400账户余额为:1400
取钱者1 取钱:400账户余额为:1000
存款者乙 存款:200账户余额为:1200
取钱者2 取钱:600账户余额为:600
3. 总结
如果取款金额大于余额则不让取款,等存款队列继续存钱,余额足够支付时再让取款。
如果存款过多(大于1000),则存款不让存了,等取款队列把钱取走,余额降低到1000以下时,可以继续存款。
这样就允许多次连续取款(只要帐户有钱),多次连续存款(余额不能大于1000),而不是存款、取款依次调用。