關(guān)于多線程同步問題我們可以借用一個(gè)銀行取錢的實(shí)例來說明問題。
在這里我們簡單的建立一個(gè)類來代表銀行賬戶稚晚,代碼如下
public class Account {
// 賬號(hào)
private String accountNo;
//余額
private double balance;
public Account(){}
// 構(gòu)造器
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;
}
// 下面兩個(gè)方法根據(jù)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;
}
}
建立一個(gè)取錢的線程類
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() + "取錢失敗俱诸!余額不足");
}
}
}
在建立一個(gè)測試類模仿2個(gè)人對(duì)同一個(gè)賬戶進(jìn)行取錢
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();
}
}
運(yùn)行結(jié)果有可能出現(xiàn)如下圖所示的情況
問題來了:賬戶余額只有1000時(shí)取出了1600塊,而且賬戶余額出現(xiàn)了負(fù)值赛糟,這不是我們希望得到的結(jié)果。
這是因?yàn)閞un()方法的方法體不具備同步安全性——程序中有兩個(gè)并發(fā)線程在修改Account對(duì)象砸逊,
為了解決這個(gè)問題璧南,java多線程引入了同步監(jiān)視器來解決這個(gè)問題,使用同步監(jiān)視器的通用方法就是同步代碼塊师逸。
synchronized (obj){
}
上面語法格式中的synchronized括號(hào)里面的obj就是同步監(jiān)視器司倚,上面代碼的含義是:線程開始執(zhí)行
同步之前,必須先獲得同步監(jiān)視器的鎖定
將上面的代碼做如下修改
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() + "取錢失敹耘取崖叫!余額不足");
}
}
}
}
運(yùn)行代碼得到如下結(jié)果: