【Java】使用鎖來控制線程的調用

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

我們可以看到只有兒子在消費透支了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章