剛從螞蟻金服面試出來的被問到的10道面試題 阿新 • • 發佈:2018-12-14 下面的例子演示了100個執行緒同時向一個銀行賬戶中存入1元錢,在沒有使用同步機制和使用同步機制情況下的執行情況。 銀行賬戶類: /** * 銀行賬戶 * @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; } } 存錢執行緒類: /** * 存錢執行緒 * @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); } } 測試類: 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)關鍵字 /** * 銀行賬戶 * @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; } } 線上程呼叫存款方法時對銀行賬戶進行同步 /** * 存錢執行緒 * @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顯示的鎖機制,為每個銀行賬戶建立一個鎖物件,在存款操作進行加鎖和解鎖的操作 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來實現同步。