這是繼java 使用synchronized, wait(), notifyAll() 實現生產者-消費者模式的下一篇。
讓我們先來看看什麼是管程。管程的英文叫Monitor, 翻譯過來時監視器的意思。爲什麼要使用管程呢? 之前使用信號量實現了同步互斥,但是信號量有個一個缺點,那就是P操作和V操作很容易搞錯,如果一個地方進行了P操作,同步代碼塊執行完畢後忘記執行V操作,那麼很容易引起死鎖、飢餓等線程問題。這個問題類似C++裏面對象的創建和銷燬,如果沒有及時delete可能會產生內存泄露,如果過早銷燬則可能產生null pointer。 那管程其實就是爲了實現信號量的功能,但是不用讓編程人員來手動操作信號量。
來看看管程的模型:
從圖中可以看到,各個線程首先等待在entry queue中,然後一次只能有一個線程被開發進入管程區去執行操作(Operations)。 當該線程不滿足某個條件(比如條件X)時,該線程被掛在該條件變量的隊列中進行等待(wait),同時會喚醒(signal)一個正在等待的線程,並且要釋放當前拿到的鎖。
有了上面的原理。我們來看看如何實現一個消費者-生產者模式。
首先我們需要一個lock,來保證線程互斥地進入管程區,然後我們需要兩個條件變量notEmpty, notFull表示容器不爲空和容器不滿兩個條件,當這個兩個條件得不到滿足時,對應的執行消費和生產的線程分別掛在這兩個條件變量上。
public class Cache {
private final static int SIZE = 100;
private int count;
private Lock lock;
private Condition notFull;
private Condition notEmpty;
public Cache() {
this.count = 0;
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
this.notFull = lock.newCondition();
}
public void produce(){
lock.lock();
while(count == SIZE){
try {
System.out.println("容器已滿,生產者阻塞");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println("生產了一個,當前數量爲: "+ count);
notEmpty.signal();
lock.unlock();
}
public void consume(){
lock.lock();
while(count == 0){
try {
System.out.println("容器已空,消費者阻塞");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println("消費了一個,當前數量爲: "+ count);
notFull.signal();
lock.unlock();
}
}
public class Consumer implements Runnable {
private Cache cache;
public Consumer(Cache cache){
this.cache = cache;
}
@Override
public void run() {
while(true){
try {
cache.consume();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Producer implements Runnable {
private Cache cache;
public Producer(Cache cache){
this.cache = cache;
}
@Override
public void run() {
while(true){
try {
cache.produce();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Cache cache = new Cache();
Producer p = new Producer(cache);
Consumer c = new Consumer(cache);
int producerCount = 4, consumerCount = 2;
for (int i = 0; i < producerCount; i++){
new Thread(p).start();
}
for (int i = 0; i < consumerCount; i++){
new Thread(c).start();
}
}
}