文章目錄
- 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.

問題描述: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兩個執行個體的鎖。
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;
}
}
}
}
Account.class由JVM加載,并且確定唯一性。
4.總結
原子性問題:本質是多個資源有一緻性要求,操作的中間狀态對外不可見。
5.課後思考
在第一個示例程式裡,我們用了兩把不同的鎖來分别保護賬戶餘額、賬戶密碼,建立鎖的時候,我們用的是:private final Object xxxLock = new Object();
如果賬戶餘額用 this.balance 作為互斥鎖,賬戶密碼用 this.password 作為互斥鎖,你覺得是否可以呢?
答案:不可用可變對象做鎖。