Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。
要注意的是鎖定和取消鎖定出現在不同作用範圍中時,必須謹慎地確保保持鎖定時所執行的所有代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。
Lock 類還可以提供與隱式監視器鎖完全不同的行爲和語義,如保證排序、非重入用法或死鎖檢測。如果某個實現提供了這樣特殊的語義,則該實現必須對這些語義加以記錄。
Lock 實例只是普通的對象,其本身可以在 synchronized 語句中作爲目標使用。獲取 Lock 實例的監視器鎖與調用該實例的任何 lock()方法沒有特別的關係。爲了避免混淆,建議除了在其自身的實現中之外,決不要以這種方式使用 Lock 實例。
除非另有說明,否則爲任何參數傳遞 null 值都將導致拋出 NullPointerException。
最近做公司的項目,遇到了一個問題,大致描述如下:做的某方面的估價,參考因素根地區、年限等有關係,在此不表。地區採用由細到粗的規則,例如先是找北京市海淀區的,然後纔是在北京市查找,如果還沒有記錄,就從華北地區找。區縣和城市的數據放置在緩存中,大區數據放置在數組。
因效率方面的考慮,採用了多線程的處理方式;
區縣和城市數據放置在線程安全的Hashtable中;
問題:最終估價有很不合理的情況—價格爲0!
考慮到資源搶佔,決定採用讀寫鎖的機制來對不安全的數組進行加鎖,代碼如下:
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
readLock.lock();
.......
readLock.unlock();
小實例
package com.entity;
public class BankCard {
private String cardid = "XZ456789";
private int balance = 10000;
public String getCardid() {
return cardid;
}
public void setCardid(String cardid) {
this.cardid = cardid;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
}
這個對象會被不同線程操作。
創建兩個線程實現接口,一個作爲兒子只消費,一個作爲父親只掙錢。可是兒子消費太快也太多,而年老的父親掙錢慢也少。不久他們家的錢就成負數了,希望用這個例子也引發一下大家都父愛的思考吧。但是消費和存錢不能同時進行,我們在結果中可以看出。
package com.thread;
import java.util.concurrent.locks.Lock;
import com.entity.BankCard;
/**
* @說明 兒子類,只消費
*/
public class Consumer implements Runnable {
BankCard bc = null;
Lock lock = null;
Consumer(BankCard bc, Lock lock) {
this.bc = bc;
this.lock = lock;
}
public void run() {
try {
while(true){
lock.lock();
System.out.print("兒子要消費,現在餘額:" + bc.getBalance() + "\t");
bc.setBalance(bc.getBalance() - 2000);
System.out.println("兒子消費2000元,現在餘額:" + bc.getBalance());
lock.unlock();
Thread.sleep(1 * 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面是另一個線程類:
package com.thread;
import java.util.concurrent.locks.Lock;
import com.entity.BankCard;
/**
* @說明 父親類,只掙錢
*/
public class Consumer2 implements Runnable {
BankCard bc = null;
Lock lock = null;
Consumer2(BankCard bc, Lock lock) {
this.bc = bc;
this.lock = lock;
}
public void run() {
try {
while(true){
lock.lock();
System.out.print("父親要存錢,現在餘額:" + bc.getBalance() + "\t");
bc.setBalance(bc.getBalance() + 500);
System.out.println("父親存入500元,現在餘額:" + bc.getBalance());
lock.unlock();
Thread.sleep(3 * 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面創建一個測試類,直接運行看效果:
package com.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.entity.BankCard;
public class MainThread {
public static void main(String[] args) {
BankCard bc = new BankCard();
Lock lock = new ReentrantLock();
ExecutorService pool = Executors.newCachedThreadPool();
Consumer cm1 = new Consumer(bc, lock);
Consumer2 cm2 = new Consumer2(bc, lock);
pool.execute(cm1);
pool.execute(cm2);
}
}
把鎖對象傳進線程,線程會使用該鎖來進行對對象的鎖定和解鎖操作。
運行效果:
兒子要消費,現在餘額:10000 兒子消費2000元,現在餘額:8000
父親要存錢,現在餘額:8000 父親存入500元,現在餘額:8500
兒子要消費,現在餘額:8500 兒子消費2000元,現在餘額:6500
兒子要消費,現在餘額:6500 兒子消費2000元,現在餘額:4500
兒子要消費,現在餘額:4500 兒子消費2000元,現在餘額:2500
父親要存錢,現在餘額:2500 父親存入500元,現在餘額:3000
兒子要消費,現在餘額:3000 兒子消費2000元,現在餘額:1000
兒子要消費,現在餘額:1000 兒子消費2000元,現在餘額:-1000
父親要存錢,現在餘額:-1000 父親存入500元,現在餘額:-500
兒子要消費,現在餘額:-500 兒子消費2000元,現在餘額:-2500
兒子要消費,現在餘額:-2500 兒子消費2000元,現在餘額:-4500
兒子要消費,現在餘額:-4500 兒子消費2000元,現在餘額:-6500
我們看到,由於兒子不斷的消費他們的錢很快花完了。
但是通過打印也可以發現,每次兒子或者父親操作時,卡里面的錢是對方已經操作完的。
如果我們把一些代碼註釋掉:
lock.lock();
lock.unlock();
那麼就沒有鎖概念,我們再來看一下運行結果:
兒子要消費,現在餘額:10000 兒子消費2000元,現在餘額:8000
父親要存錢,現在餘額:8000 父親存入500元,現在餘額:8500
兒子要消費,現在餘額:8500 兒子消費2000元,現在餘額:6500
兒子要消費,現在餘額:6500 兒子消費2000元,現在餘額:4500
兒子要消費,現在餘額:4500 兒子消費2000元,現在餘額:2500
父親要存錢,現在餘額:2500 父親存入500元,現在餘額:3000
兒子要消費,現在餘額:3000 兒子消費2000元,現在餘額:1000
兒子要消費,現在餘額:1000 兒子消費2000元,現在餘額:-1000
兒子要消費,現在餘額:-1000 兒子消費2000元,現在餘額:-3000
父親要存錢,現在餘額:-3000 父親存入500元,現在餘額:-2500
兒子要消費,現在餘額:-2500 兒子消費2000元,現在餘額:-4500
如果父親沒有鎖,而兒子拿走卡以後就鎖定不釋放,那會是什麼效果呢?
兒子要消費,現在餘額:10000 父親要存錢,現在餘額:10000 父親存入500元,現在餘額:8500
兒子消費2000元,現在餘額:8000
兒子要消費,現在餘額:8500 兒子消費2000元,現在餘額:6500
兒子要消費,現在餘額:6500 兒子消費2000元,現在餘額:4500
父親要存錢,現在餘額:4500 父親存入500元,現在餘額:5000
兒子要消費,現在餘額:4500 兒子消費2000元,現在餘額:3000
兒子要消費,現在餘額:3000 兒子消費2000元,現在餘額:1000
兒子要消費,現在餘額:1000 兒子消費2000元,現在餘額:-1000
父親要存錢,現在餘額:-1000 父親存入500元,現在餘額:-500
兒子要消費,現在餘額:-1000 兒子消費2000元,現在餘額:-2500
兒子要消費,現在餘額:-2500 兒子消費2000元,現在餘額:-4500
兒子要消費,現在餘額:-4500 兒子消費2000元,現在餘額:-6500
父親要存錢,現在餘額:-6500 兒子要消費,現在餘額:-6500 父親存入500元,現在餘額:-6000
兒子消費2000元,現在餘額:-8000
兒子要消費,現在餘額:-8000 兒子消費2000元,現在餘額:-10000
兒子要消費,現在餘額:-10000 兒子消費2000元,現在餘額:-12000
我們可以看到只有兒子在消費透支了