java基礎:線程互斥


引子

由於多線程共享同一資源(臨界資源),使得多線程程序結果會有不確定性。

怎麼解決不確定性呢?以下兩種方式可以部分控制不確定性:

線程互斥

線程同步

在熟悉一下兩個概念:

臨界區:用synchronized標記的代碼段

臨界資源:被臨界區競爭的訪問的資源

線程互斥

鎖機制

線程互斥是使用鎖機制來實現的,來看看鎖機制:

  1. 標記訪問共享資源的代碼段Java就是用synchronized來標記代碼段的,synchronized是個關鍵字),指明這段代碼將作爲一個整體以原子方式來訪問共享資源;

  2. 給被訪問的資源關聯上一把鎖;

  3. 當標記的的代碼段(臨界區)訪問共享資源(臨界資源)前,首先必須獲得對象關聯的鎖;獲得鎖後將鎖鎖閉(lock),並開始實施訪問;在標記的代碼段訪問結束後,釋放鎖;然後別的代碼段就可以訪問這個資源了。

  4. 只有對象纔有鎖基本數據類型沒有鎖。

  5. 沒有使用synchronized標記的代碼段,鎖機制不起作用。

  6. 無論是synchronized正常結束還是異常退出,都會釋放鎖。

使用格式

Synchronized標記方式有兩種:

  • synchronized(obj)area ; //obj是臨界資源【一個對象】,area是臨界區【一段代碼】。

  • synchronized方法聲明

    //比如:publicsynchronized void function(){……}

    //等價於publicvoid function(){synchronized(this){area}}(第一種表達方式)


談到線程互斥問題有個很經典的案例就是銀行存錢取錢問題,來實現一下:

/**
 * 存錢取錢問題
 * 分析:
 * 賬戶類Account   取錢線程類Saver   存錢線程類Fetcher
 * 臨界資源->賬戶類的實例   臨界區->存取錢動作
 * @author jin
 *
 */
public class TakeSavingMoney {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account account=new Account("晉瑜", 5000000);
		// 存入1000000
		(new Saver(account, 1000000)).start();
		// 取出30000
		(new Fetcher(account, 30000)).start();
		// 取出450000
		(new Fetcher(account, 450000)).start();
		// 存入50000
		(new Saver(account, 50000)).start();
	}

}
class Account{	// 賬戶類
	private String name;	// 賬戶名
	private double money;	// 賬戶餘額
	
	public Account(String name, double money) {
		// TODO Auto-generated constructor stub
		this.name=name;
		this.money=money;
	}
	public String getName(){
		return this.name;
	}
	public double getMoney(){
		return this.money;
	}
	public void put(double money){	// 存錢
		this.money+=money;
	}
	public void get(double money){	// 取錢
		this.money-=money;
	}
}

class Saver extends Thread{	//存錢類
	private Account account;	// 存錢人擁有一個賬戶
	private double money;		// 將要存錢的數
	public Saver(Account account, double money) {
		// TODO Auto-generated constructor stub
		this.account=account;
		this.money=money;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized (account) {	// account是臨界資源
			//臨界區
			System.out.println(account.getName()+"賬戶\n"+"現有:"+account.getMoney()+"元\n"+"存入:"+this.money+"元.");
			account.put(this.money);
			System.out.println("現有餘額:"+account.getMoney());
		}
	}
}

class Fetcher extends Thread{	//取錢類
	private Account account;	// 取款人持有一個賬戶
	private double money;		// 將要取錢的數
	public Fetcher(Account account, double money) {
		// TODO Auto-generated constructor stub
		this.account=account;
		this.money=money;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		synchronized (account) {	
			//臨界區
			System.out.println(account.getName()+"賬戶\n"+"現有:"+account.getMoney()+"元\n"+"取出:"+this.money+"元.");
			account.get(this.money);
			System.out.println("現有餘額:"+account.getMoney());
		}
	}
}
運行結果:
晉瑜賬戶
現有:5000000.0元
存入:1000000.0元.
現有餘額:6000000.0
晉瑜賬戶
現有:6000000.0元
存入:50000.0元.
現有餘額:6050000.0
晉瑜賬戶
現有:6050000.0元
取出:450000.0元.
現有餘額:5600000.0
晉瑜賬戶
現有:5600000.0元
取出:30000.0元.
現有餘額:5570000.0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章