對(duì)於初學(xué)者來(lái)說(shuō),執(zhí)行緒的同步化 並非是個(gè)容易理解的議題,在synchronized中隱含著物件鎖定與釋放鎖定的觀念,程式中並沒(méi)有明顯的語(yǔ)意來(lái)告知這一點(diǎn),而必須靠程式設(shè)計(jì)人員本身記憶物件的鎖定與釋放鎖定問(wèn)題。
在java.util.concurrent.locks套件中新增了Lock與Condition等類別,可以讓您明確的在程式中進(jìn)行明確的鎖定與釋放鎖定。
Lock是一個(gè)介面,其中規(guī)範(fàn)了lock()、unclock()與newCondition()三種方法:
- lock()
用來(lái)取得物件的鎖定。
- unlock()
用來(lái)釋放物件的鎖定,通常由同一個(gè)Lock物件來(lái)呼叫l(wèi)ock()與unlock()。
- newCondition()
建立一個(gè)與Lock物件相關(guān)聯(lián)的Conditon物件。
Condition是一個(gè)介面,作用是在執(zhí)行緒之間進(jìn)行溝通,就如其名稱所示,告知執(zhí)行緒目前的狀況為何,是要等待?還是通知?其規(guī)範(fàn)的幾個(gè)重要方法為:- await()
告知目前的執(zhí)行緒等待,直到被通知或中斷(interrupted)。
- signal()
通知目前等待中的一個(gè)執(zhí)行緒,從上次的等待點(diǎn)繼續(xù)執(zhí)行,類似物件的notify()方法
- signalAll()
通知目前等待中的所有執(zhí)行緒參與鎖定競(jìng)爭(zhēng),而後從上次的等待點(diǎn)繼續(xù)執(zhí)行,類似物件的notifyAll()方法。
在這邊直接改寫wait ()、notify() 中的Clerk類別,不使用synchronized、wait()、notify(),而改用Lock與Condition,其中ReentrantLock為L(zhǎng)ock介面的一個(gè)實(shí)作類別:
- Clerk.java
import java.util.concurrent.locks.*;
public class Clerk {
private Lock lock = new ReentrantLock();
private Condition threadCond = lock.newCondition();
// -1 表示目前沒(méi)有產(chǎn)品
private int product = -1;
// 這個(gè)方法由生產(chǎn)者呼叫
public void setProduct(int product) {
lock.lock();
try {
if(this.product != -1) {
try {
// 目前店員沒(méi)有空間收產(chǎn)品,請(qǐng)稍候!
threadCond.await();
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
this.product = product;
System.out.printf("生產(chǎn)者設(shè)定 (%d)%n", this.product);
// 通知等待區(qū)中的一個(gè)消費(fèi)者可以繼續(xù)工作了
threadCond.signal();
}
finally {
lock.unlock();
}
}
// 這個(gè)方法由消費(fèi)者呼叫
public int getProduct() {
lock.lock();
int p = 0;
try {
if(this.product == -1) {
try {
// 缺貨了,請(qǐng)稍候!
threadCond.await();
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
p = this.product;
System.out.printf(
"消費(fèi)者取走 (%d)%n", this.product);
this.product = -1;
// 通知等待區(qū)中的一個(gè)生產(chǎn)者可以繼續(xù)工作了
threadCond.signal();
}
finally {
lock.unlock();
}
return p;
}
}