存钱和取钱
存钱类(AddMoney类)
public class AddMoney implements Runnable {
private BankCard card;
public AddMoney(BankCard card){
this.card=card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
this.card.save(1000);
}
}
}
取钱类(SubMoney类)
public class SubMoney implements Runnable {
private BankCard card;
public SubMoney(BankCard card){
this.card=card;
}
@Override
public void run() {
for(int i=0;i<10;i++){
this.card.take(1000);
}
}
}
银行卡类(BankCard类)
public class BankCard {
//余额
private double money;
//标记
private boolean flag;//false 没有钱,可以存,不能取,true有钱,可以取,不能存
//save存
public synchronized void save(double m){//this
while(flag) {//如果有钱
//等待
try {
this.wait();//进入锁的等待队列等待,释放了cpu、锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.money=this.money+m;
System.out.println(Thread.currentThread().getName()+"存了"+m+",余额是:"+this.money);
//修改标记
this.flag=true;
//唤醒
this.notifyAll();
}
//take取
public synchronized void take(double m){//this
while(!flag){//如果没有钱
try {
this.wait();//进入锁的等待队列等待,释放了cpu、锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.money=this.money-m;
System.out.println(Thread.currentThread().getName()+"取了"+m+",余额是:"+this.money);
//修改标记
this.flag=false;
//唤醒
this.notifyAll();
}
}
测试类(TestBankCard类)
public class TestBankCard {
public static void main(String[] args) {
//创建卡
BankCard card=new BankCard();
//创建存和取
Runnable add=new AddMoney(card);
Runnable sub=new SubMoney(card);
//创建线程对象
Thread zhuxi=new Thread(add, "铸玺");
Thread xiongfei=new Thread(add,"雄飞");
Thread bingbing=new Thread(sub,"冰冰");
Thread feifei=new Thread(sub, "小菲");
//启动
zhuxi.start();
bingbing.start();
feifei.start();
xiongfei.start();
}
}
代码完善过程中注意的事项
问题1:由于线程抢占cpu是随机的,因此会产生还没有存钱,就被取走的情况。
解决办法:在方法中加入锁,并且加入锁标记,默认执行存钱线程,当存钱线程执行完毕后,修改锁标记,并用方法notify()随机唤醒其他线程。
问题2:由于wait()方法的特殊性,线程如果进入等待状态,被唤醒后会在哪等待在哪执行,wait之前的代码不在执行。
解决办法:因为锁标记是判断线程是否进入等待状态的标记,因此当线程被随机唤醒后,锁标记还是需要进行判断,如果不判断会出现多次存钱的现象,因此要加while循环,即使线程被随机唤醒也需要进行判断是否继续进入休眠状态。
出现问题的过程分析如下:
问题3:因为notify()的特性是随机唤醒,所以会造成所有线程都进入等待状态,造成死锁的情况。
解决办法:notifyAll()方法唤醒所有线程,因为线程在进等待状态的时候需要进行锁标记判断,所以唤醒所有线程不会造成影响。