Java线程安全问题-模拟对同一银行账户进行取款
本文最后更新于:2021年10月6日 晚上
Java线程安全问题-模拟对同一银行账户进行取款
代码:
package ThreadSafe;
/**
* 银行账户
* 不适用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题
*/
public class Account {
private String actNo;
private double balance;
public Account() {
}
public Account(String actNo, double balance) {
this.actNo = actNo;
this.balance = balance;
}
public String getActNo() {
return actNo;
}
public void setActNo(String actNo) {
this.actNo = actNo;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款方法
public void withdraw(double money){
//取款之前的余额
double before = this.getBalance();
//取款之后的余额
double after = before-money;
//如果加上这段模拟网络延迟,一定会出问题
//try {
//Thread.sleep(1000);
//} catch (InterruptedException e) {
//e.printStackTrace();
//}
//更新余额
//思考:t1执行到这里,但还没来得及执行这行代码,t2线程进来执行了withdraw方法,此时会发生问题.
this.setBalance(after);
}
}
package ThreadSafe;
public class AccountThread extends Thread{
//两个线程必须共享同一个账户对象
private Account act;
//通过构造方法传递过来账户对象
public AccountThread(Account act){
this.act = act;
}
@Override
public void run() {
//run方法的执行表示取款操作,假设存款有5000块
double money = 5000;
//取款,多线程并发执行这个方法
act.withdraw(money);
System.out.println(Thread.currentThread().getName()+"对"+act.getActNo()+"取款成功,余额"+act.getBalance());
}
}
package ThreadSafe;
public class Test {
public static void main(String[] args) {
//创建账户对象(只创建一个)
Account act = new Account("act-001",10000);
//创建两个线程
Thread t1 = new AccountThread(act);
Thread t2 = new AccountThread(act);
//设置name
t1.setName("t1");
t2.setName("t2");
//自动线程取款
t1.start();
t2.start();
}
}
执行结果:
加上模拟延迟代码后执行结果:
解决线程安全问题:
只需要修改Account类:
package ThreadSafe2;
/**
* 银行账户
* 不适用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题
*/
public class Account {
private String actNo;
private double balance;
public Account() {
}
public Account(String actNo, double balance) {
this.actNo = actNo;
this.balance = balance;
}
public String getActNo() {
return actNo;
}
public void setActNo(String actNo) {
this.actNo = actNo;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
//取款方法
public void withdraw(double money){
//以下这几行代码必须是线程排队的,不能并发
//一个线程把这里的代码全部执行结束之后,另一个线程才能进来
synchronized (this){
double before = this.getBalance();
double after = before-money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
}
运行结果是:
线程同步机制的语法是:
synchronized(){
//线程同步代码块
}
/*
synchronized后面小括号中传入的数据十分重要
这个数据必须是多线程共享的数据.才能达到多线程排队
()中写什么
那要看你想让那些线程同步
假设t1,t2,t3,t4,t5 有5个线程
你只希望t1,t2,t3排队,t4,t5不需要排队,
你一定要在()中写一个t1,t2,t3共享的对象,而这个
对象对于t4,t5来说不是共享的
*/
本文作者: CodeAnime
本文链接: https://codeanime.cc/Java%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98-%E6%A8%A1%E6%8B%9F%E5%AF%B9%E5%90%8C%E4%B8%80%E9%93%B6%E8%A1%8C%E8%B4%A6%E6%88%B7%E8%BF%9B%E8%A1%8C%E5%8F%96%E6%AC%BE.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!