为了解决线程安全问题,Java的多线程支持引入了同步监视器来解决这个问题,使用 同步监视器的通用方法就是同步代码块。
同步代码块的语法格式如下:
synchronized (obj)
{
//同步代码块 }
}
obj叫做同步监视器(即锁对象),任何线程进入下面同步代码块之前必须先获得对obj 的锁;其他线程无法获得锁,也就执行同步代码块。这种做法符合:“加锁修改释放锁”的逻辑。锁对象可以是任意对象,但必须保证是同一对象任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后该线程会释放对该同步监视器的锁定。
示例代码:
首先使用一个javaBean类模拟用户账户信息
package com.gx.threaddemo;
import java.io.Serializable;
publicclass Account implements Serializable {
privatestaticfinallongserialVersionUID = 1L;
// 银行账户
private String number;
// 账户余额
privatedoublemoney;
public Account(Stringnumber, double money) {
this.number = number;
this.money = money;
}
public String getNumber(){
returnnumber;
}
publicvoid setNumber(Stringnumber) {
this.number = number;
}
publicdouble getMoney() {
returnmoney;
}
publicvoid setMoney(double money) {
this.money = money;
}
}
创建一个线程类模拟取钱过程
class TakeMoney extends Thread {
// 谁取钱
private String name;
// 账户对象
private Account account;
// 取款金额
privatedoubletakeMoney;
public TakeMoney(Stringname, Account account, double takeMoney) {
this.name = name;
this.account = account;
this.takeMoney = takeMoney;
}
@Override
publicvoid run() {
// 加锁
synchronized (account) {
if (takeMoney <= account.getMoney()) {
System.out.println(this.name + "取钱成功,取出" + takeMoney + "元");
// 线程暂停 10ms 模拟网络传输
try {
sleep(10);
}catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改余额
double money = account.getMoney() - takeMoney;
System.out.println("计算余额 = " + money);
account.setMoney(money);
System.out.println("账户:" + account.getNumber() + "余额为: "
+account.getMoney());
}else {
System.out.println(this.name + "取钱失败,原因:" + account.getNumber()
+"账户余额不足!");
}
}
// 同步代码块执行结束,线程释放同步锁
}
}
创建两个线程并运行,模拟AB两人同时取钱情况
package com.gx.threaddemo;
import java.util.Random;
publicclass TakeMoneyDemo {
publicstaticvoid main(String[] args){
Accountaccount = new Account("666666", 5000);
//TakeMoney 使用了同步代码块,不存在安全问题
TakeMoneytakeMoney1 = new TakeMoney("小明", account, 1000);
TakeMoneytakeMoney2 = new TakeMoney("小红", account, 4500);
// 通过随机数,随机先启动某个程序
Randomrandom = new Random();
int randmoInt =random.nextInt(100);
if (randmoInt % 2 ==0) {
takeMoney1.start();
takeMoney2.start();
}else {
takeMoney2.start();
takeMoney1.start();
}
// 等待子线程结束
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(account.getNumber()+ "账户最终余额:" + account.getMoney());
}
}
代码执行结果如下,这样就不会出现超取的情况: