1. 程式人生 > >100個線程同時向一個銀行賬戶中存入1元錢

100個線程同時向一個銀行賬戶中存入1元錢

事情 調整 ice r12 同步機制 狀況 private nta tst

下面的例子演示了100個線程同時向一個銀行賬戶中存入1元錢,在沒有使用同步機制和使用同步機制情況下的執行情況。

  • 銀行賬戶類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 /** * 銀行賬戶 * @author 駱昊 * */ public class Account { private double balance; // 賬戶余額 /** * 存款 * @param money 存入金額 */ public
void deposit(double money) { double newBalance = balance + money; try { Thread.sleep(10); // 模擬此業務需要一段處理時間 } catch(InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } /** * 獲得賬戶余額 */
public double getBalance() { return balance; } }
  • 存錢線程類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /** * 存錢線程 * @author 駱昊 * */ public class AddMoneyThread implements Runnable { private Account account; // 存入賬戶 private double money; // 存入金額 public
AddMoneyThread(Account account, double money) { this.account = account; this.money = money; } @Override public void run() { account.deposit(money); } }
  • 測試類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Test01 { public static void main(String[] args) { Account account = new Account(); ExecutorService service = Executors.newFixedThreadPool(100); for(int i = 1; i <= 100; i++) { service.execute(new AddMoneyThread(account, 1)); } service.shutdown(); while(!service.isTerminated()) {} System.out.println("賬戶余額: " + account.getBalance()); } }

在沒有同步的情況下,執行結果通常是顯示賬戶余額在10元以下,出現這種狀況的原因是,當一個線程A試圖存入1元的時候,另外一個線程B也能夠進入存款的方法中,線程B讀取到的賬戶余額仍然是線程A存入1元錢之前的賬戶余額,因此也是在原來的余額0上面做了加1元的操作,同理線程C也會做類似的事情,所以最後100個線程執行結束時,本來期望賬戶余額為100元,但實際得到的通常在10元以下(很可能是1元哦)。解決這個問題的辦法就是同步,當一個線程對銀行賬戶存錢時,需要將此賬戶鎖定,待其操作完成後才允許其他的線程進行操作,代碼有如下幾種調整方案:

  • 在銀行賬戶的存款(deposit)方法上同步(synchronized)關鍵字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 /** * 銀行賬戶 * @author 駱昊 * */ public class Account { private double balance; // 賬戶余額 /** * 存款 * @param money 存入金額 */ public synchronized void deposit(double money) { double newBalance = balance + money; try { Thread.sleep(10); // 模擬此業務需要一段處理時間 } catch(InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } /** * 獲得賬戶余額 */ public double getBalance() { return balance; } }
  • 在線程調用存款方法時對銀行賬戶進行同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 /** * 存錢線程 * @author 駱昊 * */ public class AddMoneyThread implements Runnable { private Account account; // 存入賬戶 private double money; // 存入金額 public AddMoneyThread(Account account, double money) { this.account = account; this.money = money; } @Override public void run() { synchronized (account) { account.deposit(money); } } }
  • 通過Java 5顯示的鎖機制,為每個銀行賬戶創建一個鎖對象,在存款操作進行加鎖和解鎖的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 銀行賬戶 * * @author 駱昊 * */ public class Account { private Lock accountLock = new ReentrantLock(); private double balance; // 賬戶余額 /** * 存款 * * @param money * 存入金額 */ public void deposit(double money) { accountLock.lock(); try { double newBalance = balance + money; try { Thread.sleep(10); // 模擬此業務需要一段處理時間 } catch (InterruptedException ex) { ex.printStackTrace(); } balance = newBalance; } finally { accountLock.unlock(); } } /** * 獲得賬戶余額 */ public double getBalance() { return balance; } }

按照上述三種方式對代碼進行修改後,重寫執行測試代碼Test01,將看到最終的賬戶余額為100元。當然也可以使用Semaphore或CountdownLatch來實現同步

100個線程同時向一個銀行賬戶中存入1元錢