Java线程——生产者消费者

下面举一个两个线程共享同一个数据的例子。
先用一幅图来说明这个消费者生产者关系
这里写图片描述

ProductList是一个生产者跟消费者共享的同步机制,这个机制决定了什么情况生产者要wait(),
什么情况消费者要wait()。可以把ProductList看做一个产品仓库.当产品仓库满的时候,生产者线程需要wait(),从而放弃对产品仓库的控制。这个时候消费者线程就可以进来了且取得仓库的控制权。一旦消费者消费了产品,那么仓库就不满了,这个时候消费者线程就要notifyAll()生产者线程让等待的生产者线程唤醒。但是生产者被唤醒后不能马上进行生产,因为它在wait()的时候已经丧失了对仓库的控制权,所以就需要等待消费者线程结束操作,才能重新取得仓库的控制权,再进行生产。

所以特别注意的是,notifyAll()并不是让当前线程马上让出控制权,而只是让其他wait()当中的
线程唤醒而已,所以尽管我唤醒你,可你必须还是要等我用完仓库才能进来。相反,仓库如果空的时候,消费者线程就会wait(),然后等待生产者线程来生产产品,生产者进程乘虚而入后,让生产者线程生产产品并且唤醒消费者线程,这个情况跟上面就类似了。
(1)、产品类

public class Product {
    int id;
    private String producedBy="N/A";
    private String consumedBy="N/A";
    //构造函数,指明生产者名字
    public Product(String ProducedBy){
        this.producedBy=ProducedBy;

    }

//消费,需要指明消费者名字
    public void consume(String ConsumedBy){
        this.consumedBy=ConsumedBy;

    }

    public String getProducedBy() {
        return producedBy;
    }

    public void setProducedBy(String producedBy) {
        this.producedBy = producedBy;
    }

    public String getConsumedBy() {
        return consumedBy;
    }

    public void setConsumedBy(String consumedBy) {
        this.consumedBy = consumedBy;
    }
    @Override
    public String toString() {
        return "产品,生产者=" + producedBy + '\'' +
                ", 消费者='" + consumedBy + '\'' +
                '}';
    }
}

(2)、产品产库类
产品仓库类包含了一个大小为6的数组,表示最大的产品容量,它包含两个同步的方法。
push():生产者调用该方法加入一个新的产品。如果仓库满了,那么就使用wait()等待,直到被消费者通知后再继续添加该商品到仓库中。一旦添加了商品,就需要调用notifyAll()通知其他的消费者来消费。

pop():消费者调用该方法消费一个产品。如果仓库中没有商品,那么就使用wait()等待,直到被生产者通知后再继续消费。一旦消费了商品,就需要调用notifyAll()通知其他的生产者继续生产商品。
完整的类代码如下所示:

public class ProductList {
    int index=0;
    Product[] productList=new Product[6];
    //生产者的生产活动
    public synchronized void push(Product product){
        //如果产库满了
        while(index==productList.length){
System.out.println(" "+product.getProducedBy()+"is waitting");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //继续生产产品
        productList[index]=product;
        index++;
        System.out.println(" "+product.getProducedBy()+"生产了:"+product);

        //
        notifyAll();
        System.out.println(" "+product.getProducedBy()+"sent a notifyAll()");
    }

    //消费者的消费活动
    public synchronized Product pop(String consumeName){
        while(index==0){
            System.out.println(" "+consumeName+"is waitting");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //继续消费产品
        index--;
        Product product=productList[index];
product.consume(consumeName);
        System.out.println(index+" "+product.getConsumedBy()+"消费了"+product);

        notifyAll();

        System.out.println(" "+product.getConsumedBy()+"sent a notifyAll()");
return product;
    }
}

(3)、生产者类
该类是生产者线程类,它包含了一个仓库对象,它的主函数run()中使用while(true)循环,表明不停地在生产。生产产品调用push()加入仓库,每生产一个产品后就会休眠一个随机时间。

public class Producer implements Runnable {
    String name;
    ProductList ps = null;

    Producer(ProductList ps, String name) {
        this.ps = ps;
        this.name = name;
    }

    public void run() {
        while (true) {
            Product product = new Product(name);
            ps.push(product);
            try {
                Thread.sleep((int) (Math.random() * 300));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(4)、消费者类
该类是消费者线程类,它包含了一个仓库对象,它的主函数run()中使用while(true)循环,表明不停地在消费。消费产品调用pop()从仓库中取出商品,每消费一个产品后就会休眠一个随机时间。

public class Consumer implements Runnable {


    String name;
    ProductList ps=null;

    public Consumer(String name, ProductList ps) {
        this.name = name;
        this.ps = ps;
    }

    @Override
    public void run() {
        while(true){
            ps.pop(name);
            try {
                Thread.sleep((int) (Math.random()*300));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}

(5)、最后来一个测试类
下面新建一个测试类,首先创建一个产品仓库ProductList的对象ps,然后根据该仓库创建两个生产者px和py,并创建3个消费者
ca、cb、cc。

public class ProducerConsumer {
    public static void main(String[] args){
        ProductList ps=new ProductList();
        Producer px=new Producer(ps,"生产者x");
        Producer py=new Producer(ps,"生产者y");
        Consumer ca=new Consumer(ps,"消费者a");
        Consumer cb=new Consumer(ps,"消费者b");
        Consumer cc=new Consumer(ps,"消费者c");
        new Thread(px).start();
        new Thread(py).start();
        new Thread(ca).start();
        new Thread(cb).start();
        new Thread(cc).start();

    }
}

(6)、运行结果

启动这5个线程,这两个生产者就会不停地生产商品,消费者也会不停地消费商品,运行的结果如一下所示:

6 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
5 消费者B 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者B
  消费者B sent a notifyAll().
4 消费者C 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者C
  消费者C sent a notifyAll().
3 消费者C 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者C
  消费者C sent a notifyAll().
2 消费者B 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者B
  消费者B sent a notifyAll().
3 消费者B 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者B
  消费者B sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
5 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
4 消费者C 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者C
  消费者C sent a notifyAll().
3 消费者C 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者C
  消费者C sent a notifyAll().
2 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
1 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
2 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
1 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
0 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
1 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
2 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
3 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
3 消费者C 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者C
  消费者C sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
3 消费者A 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者A
  消费者A sent a notifyAll().
2 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
3 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
2 消费者C 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者C
  消费者C sent a notifyAll().
3 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
4 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
3 消费者C 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者C
  消费者C sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
3 消费者B 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者B
  消费者B sent a notifyAll().
2 消费者B 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者B
  消费者B sent a notifyAll().
1 消费者B 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者B
  消费者B sent a notifyAll().
2 消费者A 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者A
  消费者A sent a notifyAll().
1 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
2 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
3 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
2 消费者A 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者A
  消费者A sent a notifyAll().
3 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
3 消费者C 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者C
  消费者C sent a notifyAll().
2 消费者C 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者C
  消费者C sent a notifyAll().
1 消费者A 消费了: 产品, 生产者 = 生产者Y, 消费者 = 消费者A
  消费者A sent a notifyAll().
0 消费者B 消费了: 产品, 生产者 = 生产者X, 消费者 = 消费者B
  消费者B sent a notifyAll().
3 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
4 生产者Y 生产了: 产品, 生产者 = 生产者Y, 消费者 = N/A
  生产者Y sent a notifyAll().
5 生产者X 生产了: 产品, 生产者 = 生产者X, 消费者 = N/A
  生产者X sent a notifyAll().
  ..........

从结果来看,仓库中的产品数量在0和6之间徘徊,如果为0,则消费者等待;如果为6,则生产者等待。

生产者每生产一件商品,都会让仓库中的商品数量增加一件,会通知消费者来消费商品。
消费者侮消费一件商品,都会让仓库中的商品数量减少一件,会通知生产者来生产商品。
生产者和消费者之间共用的就是商品仓库这一个公共资源。使用该例很好的展示了资源的锁定与同步过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值