天天看點

4. 互斥鎖(下):如何用一把鎖保護多個資源 - 理論基礎1. 保護沒有關聯關系的多個資源2. 保護有關聯關系的資源3. 正确使用鎖保護多個資源4.總結5.課後思考

文章目錄

  • 1. 保護沒有關聯關系的多個資源
  • 2. 保護有關聯關系的資源
  • 3. 正确使用鎖保護多個資源
  • 4.總結
  • 5.課後思考

1. 保護沒有關聯關系的多個資源

各自建立自己鎖保護即可,如下:

class Account {
	// 鎖:保護賬戶餘額
	private final Object balLock = new Object();
	// 賬戶餘額
	private Integer balance;
	// 鎖:保護賬戶密碼
	private final Object pwLock = new Object();
	// 賬戶密碼
	private String password;

	// 取款
	void withdraw(Integer amt) {
		synchronized (balLock) {
			if (this.balance > amt) {
				this.balance -= amt;
			}
		}
	}

	// 檢視餘額
	Integer getBalance() {
		synchronized (balLock) {
			return balance;
		}
	}

	// 更改密碼
	void updatePassword(String pw) {
		synchronized (pwLock) {
			this.password = pw;
		}
	}

	// 檢視密碼
	String getPassword() {
		synchronized (pwLock) {
			return password;
		}
	}
}
           

我們也可以用目前對象作為互斥鎖,但是導緻取款,檢視密碼等操作全部程式設計串行的,影響性能。可以用細粒度鎖。

**細粒度鎖:**用不同的鎖對資源進行精細化管理,有利于提高性能。

2. 保護有關聯關系的資源

例如賬戶A減少100元,賬戶B增加100元,一個成員變量balance,一個轉賬方法transfer().

原始代碼:

class Account {
  private int balance;
  // 轉賬
  void transfer(Account target, int amt){
    if (this.balance > amt) {
      this.balance -= amt;
      target.balance += amt;
    }
  } 
}

           

直覺想用synchronized 字段修飾方法,如下:

class Account {
	// 賬戶餘額
	private int balance;
	// 轉賬
	synchronized void transfer(Account target, int amt) {
		if (this.balance > amt) {
			this.balance -= amt;
			target.balance += amt;
		}
	}
}
           

該段代碼的this鎖可以保護自己的餘額this.balance,但保護不了别人的target.balance.

4. 互斥鎖(下):如何用一把鎖保護多個資源 - 理論基礎1. 保護沒有關聯關系的多個資源2. 保護有關聯關系的資源3. 正确使用鎖保護多個資源4.總結5.課後思考

問題描述:A、B和C賬戶各有200元,A給B轉100元,B給C轉100元,希望的結果是A為100元,B為200元,C為300元。假設線程1處理A給B轉100元,線程2處理B給C轉100元,分别在兩個CPU上運作。線程1鎖定this.A執行個體,線程2鎖定this.B執行個體,兩個線程同時進入臨界區,都同時讀到B為200元,那麼最終B有可能是300元(線程1的操作結果覆寫線程2的操作)或者是100元(線程2的操作結果覆寫線程2的操作)。

本質:同個資源用了不同的鎖。這個例子中:同個資源是B的賬戶,不同的鎖是A和B兩個執行個體的鎖。

4. 互斥鎖(下):如何用一把鎖保護多個資源 - 理論基礎1. 保護沒有關聯關系的多個資源2. 保護有關聯關系的資源3. 正确使用鎖保護多個資源4.總結5.課後思考

3. 正确使用鎖保護多個資源

多個資源用共享鎖保護即可。可以用唯一對象當做共享鎖。如下代碼:

class Account {
  private Object lock;
  private int balance;
  private Account();
  // 建立 Account 時傳入同一個 lock 對象
  public Account(Object lock) {
    this.lock = lock;
  } 
  // 轉賬
  void transfer(Account target, int amt){
    // 此處檢查所有對象共享的鎖
    synchronized(lock) {
      if (this.balance > amt) {
        this.balance -= amt;
        target.balance += amt;
      }
    }
  }
}
           

上述在建立對象的時候傳入同一個對象即可。但有個問題,實踐中有可能傳入不同對象,那就出現問題。

可以用Account.class解決。這個對象是 Java 虛拟機在加載 Account 類的時候建立的,可以確定它的唯一性。

class Account {
	private int balance;

	// 轉賬
	void transfer(Account target, int amt) {
		synchronized (Account.class) {
			if (this.balance > amt) {
				this.balance -= amt;
				target.balance += amt;
			}
		}
	}
}

           
4. 互斥鎖(下):如何用一把鎖保護多個資源 - 理論基礎1. 保護沒有關聯關系的多個資源2. 保護有關聯關系的資源3. 正确使用鎖保護多個資源4.總結5.課後思考

Account.class由JVM加載,并且確定唯一性。

4.總結

原子性問題:本質是多個資源有一緻性要求,操作的中間狀态對外不可見。

5.課後思考

在第一個示例程式裡,我們用了兩把不同的鎖來分别保護賬戶餘額、賬戶密碼,建立鎖的時候,我們用的是:private final Object xxxLock = new Object();

如果賬戶餘額用 this.balance 作為互斥鎖,賬戶密碼用 this.password 作為互斥鎖,你覺得是否可以呢?

答案:不可用可變對象做鎖。