Java線程協(xié)調(diào)運行操作實例詳解
本文實例講述了Java線程協(xié)調(diào)運行操作。分享給大家供大家參考,具體如下:
一 點睛
借助于Object類提供的wait()、notify()和notifyAll()三個方法,可實現(xiàn)Java線程協(xié)調(diào)運行。這三個方法并不屬于Thread類,而是屬于Object類。但這三個方法必須同步監(jiān)視器對象調(diào)用。
關(guān)于這三個方法的解釋如下:
- wait():導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用該同步監(jiān)視器的notify()方法或notifyAll()方法來喚醒該線程。該wait()方法有三種形式:無時間參數(shù)的wait(一直等待,直到其他線程通知),帶毫秒?yún)?shù)的wait和帶毫秒、微秒?yún)?shù)的wait(這兩種方法都是等待指定時間后自動蘇醒)。調(diào)用wait()方法的當(dāng)前線程會釋放對該同步監(jiān)視器的鎖定。
- notify():喚醒在此同步監(jiān)視器上等待的單個線程。如果所有線程都在此同步監(jiān)視器上等待,則會選擇喚醒其中一個線程。選擇是任意性的。只有當(dāng)前線程放棄對該同步監(jiān)視器的鎖定后(使用wait()方法),才可以執(zhí)行被喚醒的線程。
- notifyAll():喚醒在此同步監(jiān)視器上等待的所有線程。只有當(dāng)前線程放棄對該同步監(jiān)視器的鎖定后,才可以執(zhí)行被喚醒的線程。
對于使用synchronized修飾的同步方法,因為該類的默認(rèn)實例(this)就是同步監(jiān)視器,所以可以直接調(diào)用這三個方法。
對于使用synchronized修飾的同步塊,同步監(jiān)視器是synchronized后括號里的對象,所以必須使用該對象調(diào)用這三個方法。
二 實戰(zhàn)
1 Account類
public class Account
{
// 封裝賬戶編號、賬戶余額的兩個成員變量
private String accountNo;
private double balance;
// 標(biāo)識賬戶中是否已有存款的旗標(biāo)
private boolean flag = false;
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提供getter方法,
public double getBalance()
{
return this.balance;
}
public synchronized void draw(double drawAmount)
{
try
{
// 如果flag為假,表明賬戶中還沒有人存錢進去,取錢方法阻塞
if (!flag)
{
wait();
}
else
{
// 執(zhí)行取錢
System.out.println(Thread.currentThread().getName()
+ " 取錢:" + drawAmount);
balance -= drawAmount;
System.out.println("賬戶余額為:" + balance);
// 將標(biāo)識賬戶是否已有存款的旗標(biāo)設(shè)為false。
flag = false;
// 喚醒其他線程
notifyAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
public synchronized void deposit(double depositAmount)
{
try
{
// 如果flag為真,表明賬戶中已有人存錢進去,則存錢方法阻塞
if (flag) //①
{
wait();
}
else
{
// 執(zhí)行存款
System.out.println(Thread.currentThread().getName()
+ " 存款:" + depositAmount);
balance += depositAmount;
System.out.println("賬戶余額為:" + balance);
// 將表示賬戶是否已有存款的旗標(biāo)設(shè)為true
flag = true;
// 喚醒其他線程
notifyAll();
}
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
// 下面兩個方法根據(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;
}
}
2 DrawThread線程類
public class DrawThread extends Thread
{
// 模擬用戶賬戶
private Account account;
// 當(dāng)前取錢線程所希望取的錢數(shù)
private double drawAmount;
public DrawThread(String name , Account account
, double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
// 重復(fù)100次執(zhí)行取錢操作
public void run()
{
for (int i = 0 ; i < 100 ; i++ )
{
account.draw(drawAmount);
}
}
}
3 DepositThread線程類
public class DepositThread extends Thread
{
// 模擬用戶賬戶
private Account account;
// 當(dāng)前取錢線程所希望存款的錢數(shù)
private double depositAmount;
public DepositThread(String name , Account account
, double depositAmount)
{
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
// 重復(fù)100次執(zhí)行存款操作
public void run()
{
for (int i = 0 ; i < 100 ; i++ )
{
account.deposit(depositAmount);
}
}
}
4 測試類
public class DrawTest
{
public static void main(String[] args)
{
// 創(chuàng)建一個賬戶
Account acct = new Account("1234567" , 0);
new DrawThread("取錢者" , acct , 800).start();
new DepositThread("存款者甲" , acct , 800).start();
new DepositThread("存款者乙" , acct , 800).start();
new DepositThread("存款者丙" , acct , 800).start();
}
}
三 運行結(jié)果
......
存款者甲 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者丙 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者甲 存款:800.0
賬戶余額為:800.0
四 說明
運行該程序,可以看到存款者線程、取錢者線程交替執(zhí)行的情形,每當(dāng)存款者向賬戶中存入800元之后,取錢這線程立即從賬戶中取出這筆錢。存款完成后賬戶余額總是800元,取錢結(jié)束后賬戶余額總是0元。
三個存款者線程隨機地向賬戶中存款,只有一個取錢者線程執(zhí)行取錢操作。只有當(dāng)取錢者取錢后,存款者才可以存款;同理,只有存款者存款后,取錢這線程才可以取錢。
程序最后被阻塞無法繼續(xù)向下執(zhí)行,這是因為3個存款者線程共300次存款操作,但1個取錢者線程只有100次操作,所以程序最后被阻塞。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java進程與線程操作技巧總結(jié)》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設(shè)計有所幫助。

