- 条件1:多线程并发。
- 条件2:有共享数据。
- 条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。
二、怎么解决线程安全问题?线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线程同步机制。
三、银行 取钱/存钱 案例Account 类
package ThreadSafa;
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(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
AccountThread 类
package ThreadSafa;
public class AccountThread extends Thread {
// 两个线程必须共享同一个账户对象。
private Account act;
//通过构造方法传递过来账户对象
public AccountThread(Account act) {
this.act = act;
}
@Override
public void run() {
double money = 5000;
//取款
act.withdraw(5000);
System.out.println(Thread.currentThread().getName() + "账户" + act.getActno() + "取款成功,余额" + act.getBalance());
}
}
Test 类
package ThreadSafa;
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 类 中的 withdraw 方法
package ThreadSafa;
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(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
}
四、总结
- 一个对象一把锁
- 线程拿到锁才能执行同步代码块代码



