多个线程在处理同一个资源,但是线程的任务却不相同。例如生产者和消费者模式。假设有这样的一个系统,系统要求存钱者和取钱者不断重复存钱和取钱的两个动作,每当存钱者将钱存入指定账户后,取钱者就立即取出这笔钱。如何实现这样的要求,则需要利用线程通信机制。
线程通信的方式:
- 传统的线程通信,使用synchronized关键字来保持同步
- 使用Condition来控制线程通信:使用Lock对象来保持同步
传统的线程通信:使用synchronized关键字来保持同步
借助Object类提供的wait()、notify()、notifyall()三个方法。
关于这三个方法的解释如下:
wait(): 导致当前线程等待 ,直到其他线程调用该同步监视器的notify()或者notifyAll()方法来唤醒线程
notify(): 唤醒再次同步监视器上等待的线程。如果所有的线程都在此同步监视器上等待,则会选择唤醒其中一个线程,选择是随机的。
notifyAll() : 唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对该同步监视器的锁定之后,才能执行唤醒线程操作。
那现在,我们如何去实现,重复存钱和重复取钱的操作。
public class DrawTest {
public static void main(String[] args) {
BankAccount bankAccount=new BankAccount("123bank", 0);
new DrawThread("取钱者", bankAccount, 100).start();
new DepositThread("存钱者甲", bankAccount, 100).start();
new DepositThread("存钱者乙", bankAccount, 100).start();
new DepositThread("存钱者丙", bankAccount, 100).start();
}
}
class BankAccount{
private String accountNo;
private double balance;
private boolean flag=false;
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public double getBalance() {
return balance;
}
public BankAccount(String accountNo,double balance) {
// TODO Auto-generated constructor stub
this.accountNo=accountNo;
this.balance=balance;
}
//同步方法,取钱
public synchronized void Draw(double drawingMoney) {
try {
//如果flag为false,则还没有人存钱进去,取钱方法等待
if(!flag) {
wait();
}
else {
System.out.println(Thread.currentThread().getName()+"取钱"+drawingMoney);
balance-=drawingMoney;
System.out.println("账户余额为"+balance);
flag=false;
//唤醒其他线程
notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
//同步方法,存钱
public synchronized void Deposit(double depositMoney) {
try {
//如果flag为true,说明还没有人取钱走,存钱线程等待
if(flag) {
wait();
}
else {
System.out.println(Thread.currentThread().getName()+"存款"+depositMoney);
balance+=depositMoney;
System.out.println("账户余额为"+balance);
flag=true;
//唤醒其他线程
notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
class DrawThread extends Thread{
private BankAccount bankAccount;
private double drawingMoney;
//模拟用户取钱
public DrawThread(String name,BankAccount bankAccount,double drawingMoney ) {
// TODO Auto-generated constructor stub
super(name);
this.bankAccount=bankAccount;
this.drawingMoney=drawingMoney;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++) {
bankAccount.Draw(drawingMoney);
}
}
}
class DepositThread extends Thread{
private BankAccount bankAccount;
private double depositMoney;
//模拟用户存钱
public DepositThread(String name,BankAccount bankAccount,double depositMoney ) {
// TODO Auto-generated constructor stub
super(name);
this.bankAccount=bankAccount;
this.depositMoney=depositMoney;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++) {
bankAccount.Deposit(depositMoney);
}
}
}
存钱者甲存款100.0
账户余额为100.0
取钱者取钱100.0
账户余额为0.0
存钱者丙存款100.0
账户余额为100.0
取钱者取钱100.0
账户余额为0.0
存钱者甲存款100.0
账户余额为100.0
取钱者取钱100.0
账户余额为0.0
存钱者甲存款100.0
账户余额为100.0
取钱者取钱100.0
账户余额为0.0
存钱者甲存款100.0
账户余额为100.0
取钱者取钱100.0
账户余额为0.0
存钱者甲存款100.0
账户余额为100.0