关于多线程同步问题我们可以借用一个银行取钱的实例来说明问题。
在这里我们简单的建立一个类来代表银行账户,代码如下
public class Account {
// 账号
private String accountNo;
//余额
private double balance;
public Account(){}
// 构造器
public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
}
// accountNo的setter和getter方法
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}
// balance的setter和getter方法
public void setBalance(double balance)
{
this.balance = balance;
}
public double getBalance()
{
return this.balance;
}
// 下面两个方法根据accountNo来重写hashCode()和equals()方法
public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if(this == obj)
return true;
if (obj !=null
&& obj.getClass() == Account.class)
{
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
建立一个取钱的线程类
public class DrawThread extends Thread
{
private Account account;
private double drawAmount;
public DrawThread(String name , Account account
, double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
public void run()
{
if (account.getBalance() >= drawAmount)
{
System.out.println(getName()
+ "取钱成功!吐出钞票" + drawAmount);
try
{
Thread.sleep(1);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
account.setBalance(account.getBalance() - drawAmount);
System.out.println("\t余额为:" + account.getBalance());
}
else
{
System.out.println(getName() + "取钱失败!余额不足");
}
}
}
在建立一个测试类模仿2个人对同一个账户进行取钱
public class DrawTest
{
public static void main(String[] args)
{
Account acct = new Account("1234567" , 1000);
new DrawThread("甲" , acct , 800).start();
new DrawThread("乙" , acct , 800).start();
}
}
运行结果有可能出现如下图所示的情况
问题来了:账户余额只有1000时取出了1600块,而且账户余额出现了负值,这不是我们希望得到的结果。
这是因为run()方法的方法体不具备同步安全性——程序中有两个并发线程在修改Account对象,
为了解决这个问题,java多线程引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。
synchronized (obj){
}
上面语法格式中的synchronized括号里面的obj就是同步监视器,上面代码的含义是:线程开始执行
同步之前,必须先获得同步监视器的锁定
将上面的代码做如下修改
public class DrawThread extends Thread
{
private Account account;
private double drawAmount;
public DrawThread(String name , Account account
, double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
public void run()
{
synchronized (account)
{
if (account.getBalance() >= drawAmount)
{
System.out.println(getName()
+ "取钱成功!吐出钞票:" + drawAmount);
try
{
Thread.sleep(1);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
account.setBalance(account.getBalance() - drawAmount);
System.out.println("\t余额为: " + account.getBalance());
}
else
{
System.out.println(getName() + "取钱失败!余额不足");
}
}
}
}
运行代码得到如下结果: