利用synchronized實現多執行緒同步
多執行緒程式設計帶來便利性的同時,也給我們的程式設計帶來了難度,因為多執行緒的執行具有隨機性,當多個執行緒對共享資源操作時,就很容易引發問題。
下面模擬了一個取錢的執行緒,當兩個取錢的執行緒對同一個賬戶進行操作時,我們就會發現異常。
下面是程式碼:
public class TestSyn { public static void main(String[] args) { Account a =new Account(800); WithDraw w1 = new WithDraw("取現1",a); w1.start(); WithDraw w2 = new WithDraw("取現2",a); w2.start(); } } class WithDraw extends Thread { Account a; public WithDraw(String name,Account a) { super(name); this.a = a; } public void run() { a.withdraw(800) ; } } //定義一個賬戶類,用來模擬取現 class Account{ int blance; public Account(int blance) { super(); this.blance = blance; } public int getBlance() { return blance; } public void setBlance(int blance) { this.blance = blance; } //定義取款操作 public void withdraw(int money) { if(money<=blance) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } setBlance(blance-money); System.out.println("取出"+money+"人民幣"); } else { System.out.println("餘額不足,請確認"); } } }
上面的賬戶餘額只有800快,但是我們執行程式的時候,卻發現他能連續兩次取出800快,這就是多執行緒造成的讀髒資料。為什麼會出現這種情況呢?
我們分析一下程式碼:當執行緒w1和w2執行的時候,我們假設w1先獲得了cpu的執行許可權,它呼叫run方法,執行取錢操作,執行if(money<=blance)語句,這時候money是800,賬戶a的餘額也是800,判斷條件成立,進入if裡面的程式碼塊。
這時候Thread.sleep(1000)被呼叫,w1放棄cpu使用權,w2獲得cpu使用權,執行run方法。它同樣判斷if(money<=blance)是否成立,因為之前w1執行緒還沒有呼叫setBlance函式更新賬戶餘額,所以w2執行緒也進入了if裡面的程式碼塊。
所以w1執行緒和w2執行緒都進行了取錢操作,就造成了餘額只有800塊,但是我們卻取出了1600塊錢,這種悲劇。
那麼怎麼解決這個問題呢,分析上面的程式碼,我們可以發現就是if(條件)判斷成立後,進入其執行程式碼塊時,我們最後連續執行完程式碼塊(也就是得趕緊更新餘額,或者說進入if程式碼塊後,我們應該禁止別人進入if執行的程式碼塊中了。
java中給我們提供了synchronized關鍵字來解決上面的問題。
1:利用同步程式碼塊
上面使用的是同步程式碼塊,synchronized後面跟的是鎖物件,本程式中使用this物件作為鎖物件,因為某一時刻只能有一個執行緒獲得鎖物件,所以當某個執行緒在取錢半途時間片到了,另外一個執行緒得到了執行機會,但是因為他沒有獲得鎖物件,所以他還是不能進行取現操作,上面的程式碼是靠犧牲了時間來保證了多執行緒訪問共享資源的準確性。public void withdraw(int money) { synchronized (this) { if(money<=blance) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } setBlance(blance-money); System.out.println("取出"+money+"人民幣"); } else { System.out.println("餘額不足,請確認"); } } }
2:我們還可以定義同步方法來控制執行緒的同步,同步方法預設使用this物件作為鎖物件,下面是程式碼
public synchronized void withdraw(int money) { if(money<=blance) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } setBlance(blance-money); System.out.println("取出"+money+"人民幣"); } else { System.out.println("餘額不足,請確認"); } }