11. 生產者\消費者模式
Object中的 wait()/notify()
要點:判斷條件時一定要用while()循環(虛假喚醒)
public class Shop{
public int count = 0;
public void produce(){
count++;
System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
}
public void sell(){
count--;
System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
}
public static void main(String[] args){
Shop shop = new Shop();
Factory f1 =shop.new Factory(shop);
Factory f2 = shop.new Factory(shop);
Consumer c1 =shop.new Consumer(shop);
Consumer c2 = shop.new Consumer(shop);
f1.start();
f2.start();
c1.start();
c2.start();
}
class Factory extends Thread{
private Shop shop;
Factory(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true) {
synchronized (shop) {
while (shop.count >= 10) {
try {
shop.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
shop.produce();
shop.notifyAll();
}
}
}
}
class Consumer extends Thread{
private Shop shop;
Consumer(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true){
synchronized (shop) {
while (shop.count <= 0) {
try {
shop.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
shop.sell();
shop.notifyAll();
}
}
}
}
}
ReentryLock,Condition實現
public class Shop{
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition empty = lock.newCondition();
private Condition full = lock.newCondition();
public void produce(){
count++;
System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
}
public void sell(){
count--;
System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
}
public static void main(String[] args){
Shop shop = new Shop();
Factory f1 =shop.new Factory(shop);
Factory f2 = shop.new Factory(shop);
Consumer c1 =shop.new Consumer(shop);
Consumer c2 = shop.new Consumer(shop);
f1.start();
f2.start();
c1.start();
c2.start();
}
class Factory extends Thread{
private Shop shop;
Factory(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
while (shop.count >= 10) {
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
shop.produce();
empty.signal();
} finally {
lock.unlock();
}
}
}
}
class Consumer extends Thread{
private Shop shop;
Consumer(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true){
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
while (shop.count <= 0) {
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
shop.sell();
full.signal();
} finally {
lock.unlock();
}
}
}
}
}
阻塞隊列實現
BlockingQueue即阻塞隊列,從阻塞這個詞可以看出,在某些情況下對阻塞隊列的訪問可能會造成阻塞。被阻塞的情況主要有如下兩種:
- 當阻塞隊列爲空時,從阻塞隊列中取數據的操作會被阻塞。
- 當阻塞隊列爲滿時,往阻塞隊列中添加數據的操作會被阻塞。
從上可知,阻塞隊列是線程安全的。
JDK中的七大阻塞隊列
阻塞隊列名稱 | 說明 |
---|---|
ArrayBlockingQueue | 一個由數組結構組成的有界阻塞隊列。 |
LinkedBlockingQueue | 一個由鏈表結構組成的有界阻塞隊列。 |
PriorityBlockingQueue | 一個支持優先級排序的無界阻塞隊列。 |
DelayQueue | 一個使用優先級隊列實現的無界阻塞隊列。 |
SynchronousQueue | 一個不存儲元素的阻塞隊列。 |
LinkedTransferQueue | 一個由鏈表結構組成的無界阻塞隊列。 |
LinkedBlockingDeque | 一個由鏈表結構組成的雙向阻塞隊列。 |
ArrayBlockingQueue:
基於數組的阻塞隊列實現,其內部維護一個定長的數組,用於存儲隊列元素。線程阻塞的實現是通過ReentrantLock來完成的,數據的插入與取出共用同一個鎖,因此ArrayBlockingQueue並不能實現生產、消費同時進行。而且在創建ArrayBlockingQueue時,我們還可以控制對象的內部鎖是否採用公平鎖,默認採用非公平鎖。
LinkedBlockingQueue:
基於單向鏈表的阻塞隊列實現,在初始化LinkedBlockingQueue的時候可以指定對立的大小,也可以不指定,默認類似一個無限大小的容量(Integer.MAX_VALUE),不指隊列容量大小也是會有風險的,一旦數據生產速度大於消費速度,系統內存將有可能被消耗殆盡,因此要謹慎操作。另外LinkedBlockingQueue中用於阻塞生產者、消費者的鎖是兩個(鎖分離),因此生產與消費是可以同時進行的。
BlockingQueue接口的一些方法:
操作 | 拋異常 | 特定值 | 阻塞 | 超時 |
---|---|---|---|---|
插入 | add(o) | offer(o) | put(o) | offer(o, timeout, timeunit) |
移除 | remove(o) | poll(o) | take(o) | poll(timeout, timeunit) |
檢查 | element(o) | peek(o) |
這四類方法分別對應的是:
1 . ThrowsException:如果操作不能馬上進行,則拋出異常
2 . SpecialValue:如果操作不能馬上進行,將會返回一個特殊的值,一般是true或者false
3 . Blocks:如果操作不能馬上進行,操作會被阻塞
4 . TimesOut:如果操作不能馬上進行,操作會被阻塞指定的時間,如果指定時間沒執行,則返回一個特殊值,一般是true或者false
代碼實現:
public class Shop{
private int count = 0;
private BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
public void produce() {
try {
queue.put(1);
count++;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
}
public void sell(){
try {
queue.take();
count--;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
}
public static void main(String[] args){
Shop shop = new Shop();
Factory f1 =shop.new Factory(shop);
Factory f2 = shop.new Factory(shop);
Consumer c1 =shop.new Consumer(shop);
Consumer c2 = shop.new Consumer(shop);
f1.start();
f2.start();
c1.start();
c2.start();
}
class Factory extends Thread{
private Shop shop;
Factory(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
shop.produce();
}
}
}
class Consumer extends Thread{
private Shop shop;
Consumer(Shop shop){
this.shop=shop;
}
@Override
public void run(){
while(true){
shop.sell();
}
}
}
}