(借鑒自劉望舒的Android進階之光)
這個很好了解,有些資源是不安全的,如果多個線程同時通路會引起結果的錯誤,是以我們需要給他加一個同步鎖。即synchronized關鍵字,沒有人不熟悉他的,但是還有一個重入鎖就比較冷門了。
Lock l = new ReentrantLock();
l.lock();
try {
...
} finally {
l.unlock();
}
這個鎖保證了同一時刻,這一個代碼塊(稱之為臨界區)隻有一個線程可以進入。這裡finally中解鎖是很有必要的,否則如果一旦發生異常,另外所有的線程都阻塞住了。
可是當進入臨界區的時候,發現隻有子安一個條件滿足之後,這個線程才能執行,又該怎麼辦。這個時候就可以用一個條件對象(别名條件變量)來管理那些已經獲得了一個鎖但是卻不能做有用工作的線程。
一個例子說明條件對象的重要性,比如大家都在轉賬,這個操作必須是同步的吧。但是一旦他錢不夠了怎麼轉給别人?這個時候難道另外的線程跟着一起死鎖?是以需要把這個線程轉入這個條件的等待集并置為阻塞狀态,知道另一個線程調用了同一個條件的signalAll()(這是把這個條件的所有阻塞線程都開啟,并且讓他們在外面排隊,重新競争)為止。
public void transfer(int from, int to, int amount) throws InterruptedException{
mLock.lock();
try {
while (accounts[from] < amount) {
condition.await();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
condition.signalAll();
} finally {
mLock.unlock();
}
}
while就是當錢不夠的時候,把這個線程讓條件對象來管理。下面的condition.signalAll();就是把條件對象管理的線程全部都激活,讓他們重新排隊。如果沒有線程可以進入這個臨界區,那就死鎖了。
全部代碼
class pay {
private double[] accounts;
private Lock mLock;
private Condition condition;
public pay(int n, double money) {
accounts = new double[n];
mLock = new ReentrantLock();
condition = mLock.newCondition();
for (int i = 0; i < accounts.length; i ++) {
accounts[i] = money;
}
}
public void transfer(int from, int to, int amount) throws InterruptedException{
mLock.lock();
try {
while (accounts[from] < amount) {
condition.await();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
condition.signalAll();
} finally {
mLock.unlock();
}
}
}
不過用synchronized鎖住一個方法是更友善的選擇
public synchronized void method(){
...
}
用synchronized關鍵字來改寫上面的transfer方法
public synchronized void transfer(int from, int to, int amount) throws InterruptedException {
while (accounts[from] < amount) {
wait();
}
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
notifyAll();
}
簡潔很多。使用synchronized,你必須了解到每一個對象有一個内部所,并且這個鎖有一個内部條件。wait().notifyAll()用法和之前的類似。
synchronized也可以通過使用同步代碼塊來獲得這個鎖。
synchronized(obj) {
}
再用這個方法改寫一下
public synchronized void transfer(int from, int to, int amount) throws InterruptedException {
synchronized((new Object())) {
accounts[from] = accounts[from] - amount;
accounts[to] = accounts[to] + amount;
}
}
這種方法不推薦用,因為僅僅隻能鎖住括号裡的對象,代碼塊是鎖不了的!一般同步實作最好用comcurrent并發包下的類。