共享数据:产品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();
}
}
}
这里只是个人的理解和看法,希望对读者有所帮助,有不正确或者有歧义之处请及时指出我们可相互探讨