文章目录
问题引入
不考虑线程同步程序运行后的结果:
小明取钱100000.0
小红取钱100000.0
小明取完后,余额:0.0
小红取完后,余额:-100000.0
银行亏钱了?嘿嘿,我有一个大胆的想法
代码
java
class Test {
public static void main(String[] args) {
account acc = new account("ICBC-110",100000);
new drawThread(acc,"小明").start();
new drawThread(acc,"小红").start();
}
}
class account {
private String ID;
private double money;
public account() {
}
public account(String ID, double money) {
this.ID = ID;
this.money = money;
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawMoney(double money) {
//区分谁来取钱
String name = Thread.currentThread().getName();
//取钱步骤
if (this.money >= money) {
System.out.println(name+"取钱"+money);
this.money -= money;
System.out.println(name+"取完后,余额:"+this.money);
}else {
System.out.println(name +"来取钱,余额不足~");
}
}
}
class drawThread extends Thread{
private account acc;
public drawThread(account acc,String name)
{
super(name);
this.acc = acc;
}
@Override
public void run() {
// 取钱
acc.drawMoney(100000);
}
}
线程安全问题出现的原因是什么?
- 存在多个线程在同时执行:两个人同时取钱
- 同时访问一个共享资源:同一个账户
- 存在修改该共享资源:修改余额
那我们应该怎么解决这一问题呢。
方法一:同步代码块
synchronized 就是一个锁,每当一个线程运行到这里时会给这个线程加锁,然后才能执行同步代码块里面的代码,执行完后再进行解锁,让下一个进行重复当前步骤。
java
public void drawMoney(double money) {
//区分谁来取钱
String name = Thread.currentThread().getName();
synchronized ("取钱") {
//取钱步骤
if (this.money >= money) {
System.out.println(name+"取钱"+money);
this.money -= money;
System.out.println(name+"取完后,余额:"+this.money);
}else {
System.out.println(name +"来取钱,余额不足~");
}
}
}
改进
"取钱"是一个字符串,在常量池里面,就一份,假如又多了俩人小黑和小白,这俩人操作的是同一个账户,那这样的话定义成 "取钱" 这一对象就不能达到想要效果(执行效果是小红或者小明取钱的时候小黑或者小白也不能取钱)
java
public void drawMoney(double money) {
//区分谁来取钱
String name = Thread.currentThread().getName();
synchronized (this) {
// 这里改成this,原来的字符串防的是所有的,比如小红取,小黑、白也不能取![请添加图片描述](https://img-blog.csdnimg.cn/5c8eba6dfa7f40e7ac2ce2ab79057669.png)
//取钱步骤
if (this.money >= money) {
System.out.println(name+"取钱"+money);
this.money -= money;
System.out.println(name+"取完后,余额:"+this.money);
}else {
System.out.println(name +"来取钱,余额不足~");
}
}
}
方法二:同步方法
java
public synchronized void drawMoney(double money) {
//区分谁来取钱
String name = Thread.currentThread().getName();
//取钱步骤
if (this.money >= money) {
System.out.println(name+"取钱"+money);
this.money -= money;
System.out.println(name+"取完后,余额:"+this.money);
}else {
System.out.println(name +"来取钱,余额不足~");
}
}
方法三:lock
java
private Lock lk = new ReentrantLock();
每个账户对象都应该有一个锁对象。方便锁住访问同一对象的线程
java
public void drawMoney(double money) {
//区分谁来取钱
String name = Thread.currentThread().getName();
lk.lock();
try {
//取钱步骤
if (this.money >= money) {
System.out.println(name+"取钱"+money);
this.money -= money;
System.out.println(name+"取完后,余额:"+this.money);
}else {
System.out.println(name +"来取钱,余额不足~");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lk.unlock();
}
}