100個線程同時向一個銀行賬戶中存入1元錢
阿新 • • 發佈:2018-06-19
事情 調整 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元錢