1. 程式人生 > >生產者與消費者問題

生產者與消費者問題

bject i++ sig notify ktr nal 消費者 默認 等待

共享數據:產品product(該變量的狀態是可變的)

一個存放產品的容器

生產者(一個或者多個):共同訪問共享數據product,並可能進行修改更新操作

消費者(一個或者多個):共同訪問共享數據product,並可能進行修改更新操作

問題:

  條件:一個容器,0個或多個產品(產品數量由生產者消費者控制),一個容器最大容量限制,生產者可能生產的最大值或不斷生產,消費者可能消費的最大值或一直消費

  1.多個生產者在生產商品時可能會出現競爭容器存放條件資源(比如:這個容器最大存放量為10,已結有9個商品存在,這時可能有多個生產者為了占用最後一個資源而進行搶占或者說競爭)

  2.多個消費者也可能為了競爭最後一個或者某個同一產品而出現問題

3.消費者者消費的同時,生產者也在生產(即:不斷輪詢)---消費者等待生產者生產,生產者等待消費者消費

解決辦法:

通過等待喚醒機制:

第一種方式:通過傳統的同步方法和Object,wait(),notify(),notifyAll()方法

代碼及解析如下:

package com.qiang.juc.test;

/**
* 生產者---消費者
* synchronized保證同步鎖,保證通信
* @author SuperQiang
*/
public class ProductorAndConsumerTestSyn {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);

new Thread(productor,"生產者1號").start();
new Thread(productor,"生產者2號").start();
new Thread(productor,"生產者3號").start();
new Thread(consumer,"消費者1號").start();
new Thread(consumer,"消費者2號").start();
new Thread(consumer,"消費者3號").start();
}
}

/**
* 店員(相當於入貨口,和出貨口)===(添加新數據和刪除舊數據)
* @author SuperQiang
*
*/
class Clerk{

/**
* 當前庫存(產品數0,為空)
* 共享變量(可變狀態的變量)
*/
private int product=0;

/**
* 進貨
* 添加商品(即生產者生產商品)
* 這裏庫存默認10
*/
public synchronized void addProduct(){
while(product>=1){//判斷當前庫存是否與最大值庫存相同
//如果相同則提示店員庫存已滿,請生產者停止生產
System.out.println("庫存已滿");
try {
//如果庫存已滿,生產者停止等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//這裏默認每次生產者只能生產一件商品(即每次添加一個)
System.out.println(Thread.currentThread().getName()+":"+ ++product);
//如果生產者已經停止生產商品則喚醒所有消費者線程開始消費商品
this.notifyAll();
}

/**
* 賣貨
* 消費商品(即消費者消費商品--每次默認可消費一件)
* 這裏庫存默認10
*/
public synchronized void consumerProduct(){
while(product<=0){//判斷當前庫存是否為空或者可能導致庫存為負數的現象
//如果是,則提示消費者該商品庫存已為空,停止消費
System.out.println("庫存為空");
try {
this.wait();//如果庫存已經為空,則停止消費
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//消費者可消費(這裏模擬每次一個消費之只能消費一件商品)
System.out.println(Thread.currentThread().getName()+":"+ --product);
//如果消費者已經沒有商品可消費則喚醒所有生產者線程開始生產商品
this.notifyAll();
}
}

/**
* 生產者(可能有多個,也可能一個)---多個生產者同時生產一個商品,並將其放到同一個容器中
* 即:多線程共享一個數據,每個線程都每個時段都可能會對修改該數據
* @author SuperQiang
*
*/
class Productor implements Runnable{

/**
* 同一個店員對象
* 即:每個生產者同時對一個店員的同一個商品進行生產
* 註:這裏的店員就相當於一個代理的過程容器量
*/
private Clerk clerk;

public Productor(Clerk clerk){
this.clerk = clerk;
}

@Override
public void run() {
//默認假設這裏的生產者最多只能生產20件這樣的商品
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//生產者生產商品,並添加到店員對象對應的商品存放容器中
clerk.addProduct();
}
}
}

/**
* 消費者(可能有多個,也可能一個)---多個消費同時消費同一個存放相同商品容器中的商品
* 即:多線程共享一個數據,每個線程都每個時段都可能會對修改該數據
* @author SuperQiang
*
*/
class Consumer implements Runnable{

/**
* 同一個店員對象
* 即:每個生產者同時對一個店員的同一個商品進行生產
* 註:這裏的店員就相當於一個代理的過程容器量
*/
private Clerk clerk;

public Consumer(Clerk clerk){
this.clerk = clerk;
}

@Override
public void run() {
//默認假設這裏的消費者最多只能消費20件這樣的商品
for (int i = 0; i < 20; i++) {
//消費者消費商品
clerk.consumerProduct();
}
}
}

第二種方式:通過同步鎖lock(顯式鎖),Condition接口中的await(),signal(),signalAll()方法來控制線程通信

package com.qiang.juc.test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 生產者---消費者
* synchronized保證同步鎖,保證通信
* @author SuperQiang
*/
public class ProductorAndConsumerCondition {
public static void main(String[] args) {
Clerk1 clerk = new Clerk1();
Productor1 productor = new Productor1(clerk);
Consumer1 consumer = new Consumer1(clerk);

new Thread(productor,"生產者1號").start();
new Thread(productor,"生產者2號").start();
new Thread(productor,"生產者3號").start();
new Thread(consumer,"消費者1號").start();
new Thread(consumer,"消費者2號").start();
new Thread(consumer,"消費者3號").start();
}
}

/**
* 店員(相當於入貨口,和出貨口)===(添加新數據和刪除舊數據)
* @author SuperQiang
*
*/
class Clerk1{

/**
* 當前庫存(產品數0,為空)
* 共享變量(可變狀態的變量)
*/
private int product=0;

private Lock lock = new ReentrantLock();

private Condition condition = lock.newCondition();
/**
* 進貨
* 添加商品(即生產者生產商品)
* 這裏庫存默認10
*/
public void addProduct(){
lock.lock();
try{
while(product>=1){//判斷當前庫存是否與最大值庫存相同
//如果相同則提示店員庫存已滿,請生產者停止生產
System.out.println("庫存已滿");
try {
//如果庫存已滿,生產者停止等待
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//這裏默認每次生產者只能生產一件商品(即每次添加一個)
System.out.println(Thread.currentThread().getName()+":"+ ++product);
//如果生產者已經停止生產商品則喚醒所有消費者線程開始消費商品
condition.signalAll();
}finally{
lock.unlock();
}
}

/**
* 賣貨
* 消費商品(即消費者消費商品--每次默認可消費一件)
* 這裏庫存默認10
*/
public void consumerProduct(){
lock.lock();
try{
while(product<=0){//判斷當前庫存是否為空或者可能導致庫存為負數的現象
//如果是,則提示消費者該商品庫存已為空,停止消費
System.out.println("庫存為空");
try {
condition.await();//如果庫存已經為空,則停止消費
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//消費者可消費(這裏模擬每次一個消費之只能消費一件商品)
System.out.println(Thread.currentThread().getName()+":"+ --product);
//如果消費者已經沒有商品可消費則喚醒所有生產者線程開始生產商品
condition.signalAll();
}finally{
lock.unlock();
}
}
}

/**
* 生產者(可能有多個,也可能一個)---多個生產者同時生產一個商品,並將其放到同一個容器中
* 即:多線程共享一個數據,每個線程都每個時段都可能會對修改該數據
* @author SuperQiang
*
*/
class Productor1 implements Runnable{

/**
* 同一個店員對象
* 即:每個生產者同時對一個店員的同一個商品進行生產
* 註:這裏的店員就相當於一個代理的過程容器量
*/
private Clerk1 clerk;

public Productor1(Clerk1 clerk){
this.clerk = clerk;
}

@Override
public void run() {
//默認假設這裏的生產者最多只能生產20件這樣的商品
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//生產者生產商品,並添加到店員對象對應的商品存放容器中
clerk.addProduct();
}
}
}

/**
* 消費者(可能有多個,也可能一個)---多個消費同時消費同一個存放相同商品容器中的商品
* 即:多線程共享一個數據,每個線程都每個時段都可能會對修改該數據
* @author SuperQiang
*
*/
class Consumer1 implements Runnable{

/**
* 同一個店員對象
* 即:每個生產者同時對一個店員的同一個商品進行生產
* 註:這裏的店員就相當於一個代理的過程容器量
*/
private Clerk1 clerk;

public Consumer1(Clerk1 clerk){
this.clerk = clerk;
}

@Override
public void run() {
//默認假設這裏的消費者最多只能消費20件這樣的商品
for (int i = 0; i < 20; i++) {
//消費者消費商品
clerk.consumerProduct();
}
}
}

這裏只是個人的理解和看法,希望對讀者有所幫助,有不正確或者有歧義之處請及時指出我們可相互探討

生產者與消費者問題