同步方法同步块Synchronized
synchronized可以加在方法上,使方法变成同步方法。也可以当成同步块使用。
public class UnSafeBank {
public static void main(String[] args) {
//创建一个账户
Account account = new Account("蓝色的番茄", 10000);
//创建两个取钱线程
Drawing you = new Drawing(account, 5000, "你");
Drawing gilrFriend = new Drawing(account, 8000, "女朋友");
//取钱
you.start();
gilrFriend.start();
}
}
//账户
class Account {
private String name;
private int salaryMoney;
public Account(String name, int salaryMoney) {
this.name = name;
this.salaryMoney = salaryMoney;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalaryMoney() {
return salaryMoney;
}
public void setSalaryMoney(int salaryMoney) {
this.salaryMoney = salaryMoney;
}
}
//银行取钱
class Drawing extends Thread {
//账户
private Account account;
//取多少钱
private int drawingMoney;
public Drawing(Account account, int drawingMoney, String name) {
//调用父类方法设置名字
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//判断能否取钱
if (account.getSalaryMoney() - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
return;
}
//模拟取钱时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置账户余额
account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);
//取钱成功,并打印
System.out.println("卡里剩余余额" + account.getSalaryMoney());
System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
}
}
上述代码中Account是一个账户类,包含账户名和余额。Drawing是银行取钱,包含账户和取多少钱。账户里有10000元,你取5000,女朋友取8000。执行代码就会出现线程不安全的情况。
在run方法上加Synchronized修饰符
@Override
public synchronized void run() {
//判断能否取钱
if (account.getSalaryMoney() - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
return;
}
//模拟取钱时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置账户余额
account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);
//取钱成功,并打印
System.out.println("卡里剩余余额" + account.getSalaryMoney());
System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
}
这里的run方法是一个普通方法,使用Synchronized之后锁的是this对象,两个this分别是you和gilrFriend。因为它们上锁的不是同一个对象,所以加了Synchronized任然无法达到线程安全。
使用同步块,里面放this对象
synchronized (this) {
//判断能否取钱
if (account.getSalaryMoney() - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
return;
}
//模拟取钱时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置账户余额
account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);
//取钱成功,并打印
System.out.println("卡里剩余余额" + account.getSalaryMoney());
System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
}
使用这种方法依然不能保证线程安全,因为两个this是指方法的调用者,即you和gilrFriend,锁的是两个不同的对象。
使用同步块,里面放具体对象
synchronized (account) {
//判断能否取钱
if (account.getSalaryMoney() - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "取钱" + drawingMoney + "余额不足");
return;
}
//模拟取钱时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置账户余额
account.setSalaryMoney(account.getSalaryMoney() - drawingMoney);
//取钱成功,并打印
System.out.println("卡里剩余余额" + account.getSalaryMoney());
System.out.println(Thread.currentThread().getName() + "取到了" + drawingMoney);
}
这里you和gilrFriend对象里的account是同一个对象,因此上锁之后另一个线程就无法进入。保证了线程的安全性。