最近在複習Java基礎,看到多線程這塊順便寫寫多線程的協調控制程序。
需求:假設系統中有兩個線程分別代表取款者和存款者,現在系統的要求是存款者和取款者不斷的重複存、取款操作,
並且要求每當有存款者將錢存入指定賬戶中時,取款者就立即取出這筆錢,即不允許存款者連續兩次存錢,也不允許
取款者兩次取錢。
下面代碼實現:
1.首先是賬戶Account類;
package com.xjtu.cruise.soft.thread;
public class Account {
/**
* @author Cruise
* @param args
*/
private String accountNo;
//標識賬戶是否還有存款的
private boolean flag = false;
private double balance;
public Account(){}
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public double getBalance(){
return balance;
}
public synchronized void draw(double drawAmount) {
try {
if (!flag) { //如果賬戶沒錢
wait(); //阻塞當前的方法,並釋放同步鎖(這裏就是this,也就是調用此方法的對象)
} else {
System.out.println("線程:" + Thread.currentThread().getName()
+ "取款" + drawAmount);
balance -= drawAmount;
System.out.println("賬戶餘額:" + balance);
flag = false;
notifyAll(); //喚醒等待此同步鎖的所有線程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void deposit(double depositAmount) {
try{
if(flag){ //如果賬戶有存入的錢
wait();
}else{
System.out.println("線程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
balance += depositAmount;
System.out.println("賬戶餘額:" + balance);
flag = true;
notifyAll();
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
2.接下來是存取款線程類(DrawThread,DepositThread)
package com.xjtu.cruise.soft.thread;
public class DepositThread extends Thread {
private Account account;
private double depositAmount;
public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
}
public void run(){
for(int i=0; i<100; i++){
// System.out.println("線程:" + Thread.currentThread().getName()+"執行存款操作。");
account.deposit(depositAmount);
}
}
}
package com.xjtu.cruise.soft.thread;
public class DrawThread extends Thread {
/**
* @author Cruise
* @param args
*/
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(){
for(int i=0; i<100; i++){
// System.out.println("線程:" + Thread.currentThread().getName()+"執行取錢操作。");
account.draw(drawAmount);
}
}
}
3.奉上測試類TestDraw
package com.xjtu.cruise.soft.thread;
public class TestDraw {
/**
* @author Cruise
* @param args
*/
public static void main(String[] args) {
Account account = new Account("123", 0);
new DrawThread("取錢者", account, 800).start();
new DepositThread("存款者甲",account, 800).start();
new DepositThread("存款者乙",account, 800).start();
}
}
OK, 一個使用wait和notifyAll控制線程協調的Demo完成,運行結果如下:
下面再使用條件變量的方式來協調線程的同步,具體來說就是使用Lock對象來保證同步,而不是用Synchronized關鍵字。
通過Lock實例來調用其newCondition()方法得到對應的Condition對象,接着使用此Condition實例的方法來保證線程的協
調。這裏用到了Condition的await和signalAll方法。
將上面的Account類修改如下,其他的線程類不變。
package com.xjtu.cruise.soft.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
/**
* @author Cruise
* @param args
*/
private final Lock lock = new ReentrantLock();//定義Lock對象
private final Condition cond = lock.newCondition();//獲取Lock對象對應的條件變量
private String accountNo;
//標識賬戶是否還有存款的
private boolean flag = false;
private double balance;
public Account(){}
public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
}
public double getBalance(){
return balance;
}
public void draw(double drawAmount) {
lock.lock();//加鎖
try {
if (!flag) { //如果賬戶沒錢
cond.await();
} else {
System.out.println("線程:" + Thread.currentThread().getName()
+ "取款" + drawAmount);
balance -= drawAmount;
System.out.println("賬戶餘額:" + balance);
flag = false;
cond.signalAll(); //喚醒該Lock對象對應的其他線程
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();//使用finally塊確保釋放鎖
}
}
public void deposit(double depositAmount) {
lock.lock();//加鎖
try{
if(flag){ //如果賬戶有存入的錢
cond.await();
}else{
System.out.println("線程:"+ Thread.currentThread().getName()+"存款"+depositAmount);
balance += depositAmount;
System.out.println("賬戶餘額:" + balance);
flag = true;
cond.signalAll();//喚醒該Lock對象對應的其他線程
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{//使用finally塊確保釋放鎖
lock.unlock();
}
}
}
運行上面的測試類,可以得到如下的運行結果:
從上面的兩個運行結果可以發現,雖然兩種方法都可以保證線程的同步和協調運行,但是線程的調度機制還是有些許的
差別,這點可以從上圖的紅框看出。當然底層的具體實現,因爲不大瞭解,這裏就不作具體的闡述了(廢話,不瞭解當
然闡述不了,^_^),希望瞭解的同學能幫忙解答下,very thx!