題目:類比銀行取款,使用三種線程同步方法實現取款的安全。編寫如下類,並進行取款測試:
賬戶類:Account,含餘額欄位balance
取款類:DrawMoney,在指定帳戶中進行取款操作
編寫線程體:實現取款操作的安全
方法1:同步代碼塊
/** * 銀行帳戶 */class Account {/** * 餘額 */private double balance;/** * 構造方法 * @param balance */Account(double balance) {super();this.balance = balance;}//getter . setterpublic double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}}class ATM implements Runnable{/** * 需要取的金額 */private double drawMoney;String str = new String("");//做同步代碼塊的監聽對象/** * 帳戶 */private Account a;ATM(double drawMoney, Account a) {super();this.drawMoney = drawMoney;this.a = a;}public void run() {synchronized (str) {if(a.getBalance()>=drawMoney){System.out.println(Thread.currentThread().getName()+"吐出了"+drawMoney+"元");a.setBalance(a.getBalance()-drawMoney);//修改餘額}else{System.out.println("Your account don't have enough money!");}}}}public class DrawMoneyDemo {public static void main(String[] args) {Account a = new Account(1000);ATM atm = new ATM(800,a);//從a帳戶中取800元//假設有兩個線程進行同時操作new Thread(atm, "A").start();new Thread(atm, "B").start();}}
執行結果為:
B吐出了800.0元
Your account don't have enough money!
我們將具有原子性的代碼放入 synchronized 代碼塊中,形成同步代碼塊。這樣,在同一時刻只能有一個線程進入代碼塊執行該段代碼。
格式為:
synchronized(object){
//代碼塊
}
方法2:同步方法
在帳戶類中定義一個同步方法實現取款操作,這樣線程執行時,只能互斥的調用該方法
/** * 銀行帳戶 */class Account {/** * 餘額 */private double balance;/** * 構造方法 * * @param balance */Account(double balance) {super();this.balance = balance;}// getter . setterpublic double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}/** * 使用同步方法的方式進行取款 * @param drawMoney * 取款金額 */public synchronized void draw(double drawMoney) {if (this.getBalance() >= drawMoney) {System.out.println(Thread.currentThread().getName() + "吐出了"+ drawMoney + "元");this.setBalance(this.getBalance() - drawMoney);// 修改餘額} else {System.out.println("Your account don't have enough money!");}}}class ATM implements Runnable {/** * 需要取的金額 */private double drawMoney;/** * 帳戶 */private Account a;ATM(double drawMoney, Account a) {super();this.drawMoney = drawMoney;this.a = a;}public void run() {a.draw(drawMoney);}}public class DrawMoneyDemo {public static void main(String[] args) {Account a = new Account(1000);ATM atm = new ATM(800, a);// 從a帳戶中取800元// 假設有兩個線程進行同時操作new Thread(atm, "A").start();new Thread(atm, "B").start();}}
執行結果為:
A吐出了800.0元
Your account don't have enough money!
方法3:利用Java5的同步鎖機制實現線程同步
import java.util.concurrent.locks.ReentrantLock;/** * 銀行帳戶 */class Account {/** * 餘額 */private double balance;// 定義可重新進入鎖private final ReentrantLock lock = new ReentrantLock();/** * 構造方法 * * @param balance */Account(double balance) {super();this.balance = balance;}// getter . setterpublic double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}/** * 使用可重新進入鎖的方式進行取款 * * @param drawMoney * 取款金額 */public void draw(double drawMoney) {lock.lock();// 加鎖try {if (this.getBalance() >= drawMoney) {System.out.println(Thread.currentThread().getName() + "吐出了"+ drawMoney + "元");this.setBalance(this.getBalance() - drawMoney);// 修改餘額} else {System.out.println("Your account don't have enough money!");}} finally {lock.unlock();// 釋放鎖}}}class ATM implements Runnable {/** * 需要取的金額 */private double drawMoney;/** * 帳戶 */private Account a;ATM(double drawMoney, Account a) {super();this.drawMoney = drawMoney;this.a = a;}public void run() {a.draw(drawMoney);}}public class DrawMoneyDemo {public static void main(String[] args) {Account a = new Account(1000);ATM atm = new ATM(800, a);// 從a帳戶中取800元// 假設有兩個線程進行同時操作new Thread(atm, "A").start();new Thread(atm, "B").start();}}
執行結果為:
A吐出了800.0元
Your account don't have enough money!
對於可重新進入鎖,其格式為:
public class X {private final ReentrantLock lock = new ReentrantLock();//定義需要保證安全執行緒的方法public void m(){//加鎖lock.lock();try{//... method body}finally{//在finally釋放鎖lock.unlock();}}}
使用ReentrantLock可重新進入鎖對象可以顯示的加鎖和解鎖,其具有與使用synchronized方法和語句所訪問的隱式監聽器鎖相同的一些基本行為和語義,但功能更強大。