JAVA多线程实现简单的一对一生产者消费者模式(三种方法)

通过对JAVA多线程的学习,自己手写了三种方式,实现简单的一对一生产者消费者模式,每个案例中都有一个消费者线程,一个生产者线程和一个存储产品的类。具体如下。
PS:目前只实现了三种方法,如果有更多的,十分欢迎大家补充,一起交流。

1 采用wait()和notify()的结合方法

wait()和notify()需要结合sychronized使用,wait()负责释放锁,挂起线程,等待其他线程notify()方法唤醒该挂起的线程。具体代码如下:

package com.blog.Thread1;

/**
 * 使用wait()和notify()实现一对一生产者消费者模式
 */
public class ThreadTest {
    public static void main(String[] args) {
        //创建用于存放产品的对象
        Product product = new Product();
        //创建生产者线程对象
        Thread producer = new Producer(product);
        //创建消费者线程对象
        Thread customer = new Customer(product);

        //必须先开启生产者,再开启消费者
        producer.start();
        customer.start();
    }
}

//用字符串模拟产品
class Product{
    // 用于存放产品,若为空字符串,代表没有产品
    private String product = "";

    //获得产品
    public String getProduct() {
        return product;
    }

    //生产产品
    public void setProduct(String product){
        this.product = product;
    }
}
//模拟消费者
class Customer extends Thread{

    private Product product;
    //构造方法
    public Customer(Product product){
        this.product = product;
    }

    @Override
    public void run() {
        try {
            //不断从Product中获取产品进行消费
            while(true) {
                //对product加锁
                synchronized (product) {
                    if (product.getProduct().equals("")){
                        //空字符串代表还没有产品,就进行等待
                        product.wait();
                    }
                    //执行到这里说明生产者生产了产品,此时消费者可以拿到
                    String product_str = product.getProduct();
                    System.out.println("消费者:消费了产品--" + product_str);
                    //将product中的产品设置为空,表示消费者已经消费产品
                    product.setProduct("");
                    Thread.sleep(1000);//模拟延时的效果
                    product.notify();//通知生产者该生产产品了
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//模拟生产者
class Producer extends Thread{
    private Product product;
    //构造方法
    public Producer(Product product){
        this.product = product;
    }

    @Override
    public void run() {
        try {
            //为了区别产品,辅助添加的变量
            int i = 1;
            //不断从Product中获取产品进行消费
            while(true) {
                //对product加锁
                synchronized (product) {
                    if (!product.getProduct().equals("")){
                        //字符串不为空,说明消费者还没有消费产品
                        //那么就等待消费者消费之后再生产产品
                        product.wait();
                    }
                    //执行到这里说明消费者消费了产品,那么就生产产品
                    product.setProduct("产品" + i);
                    System.out.println("生产者:生产了产品--" + "产品" + i);
                    i ++;
                    Thread.sleep(1000); //模拟延时的效果
                    product.notify();//通知消费者消费产品
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
程序运行结果:
生产者:生产了产品--产品1
消费者:消费了产品--产品1
生产者:生产了产品--产品2
消费者:消费了产品--产品2
生产者:生产了产品--产品3
消费者:消费了产品--产品3
生产者:生产了产品--产品4
消费者:消费了产品--产品4
生产者:生产了产品--产品5
消费者:消费了产品--产品5
生产者:生产了产品--产品6
消费者:消费了产品--产品6
生产者:生产了产品--产品7
消费者:消费了产品--产品7

2 采用Condition实现

Condition需要结合Lock使用,因为只能通过Lock获取Condition对象。具体代码实现如下:

/**
 * 使用Condition实现一对一生产者消费者模式
 */
public class ThreadTest {
    public static void main(String[] args) {
        //创建用于存放产品的对象
        ProductService productService = new ProductService();
        //创建生产者线程对象
        Thread producer = new Producer(productService);
        //创建消费者线程对象
        Thread customer = new Customer(productService);

        //必须先开启生产者,再开启消费者
        producer.start();
        customer.start();
    }
}

//用字符串模拟产品
class ProductService{
    //创建锁
    Lock lock = new ReentrantLock();
    //创建Condition对象
    Condition condition = lock.newCondition();

    // 用于存放产品,若为空字符串,代表没有产品
    public String product = "";

    //获得产品
    public void getProduct() {
        try {
            lock.lock();
            if (this.product.equals("")){
                //如果product为空字符串,说明没有产品
                //调用Condition的await()方法挂起线程
                //等待生产者生产产品
                condition.await();
            }
            //运行到这里说明获得了产品
            System.out.println("消费者:消费了产品--" + this.product);
            //将product设置为空
            this.product = "";
            condition.signal();//通知生产者生产产品
            Thread.sleep(1000); //模拟延时效果
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //生产产品
    public void setProduct(){
        try {
            lock.lock();
            if (!this.product.equals("")){
                //如果product不为空字符串,说明消费者还没有消费,
                //那么挂起线程等待消费者消费产品
                condition.await();
            }
            //运行到这里说明消费者消费了产品
            //生产者生产产品    用Random产生随机数,区分不同的产品
            this.product = "产品" + new Random().nextInt(100);
            System.out.println("生产者:生产了产品--" + this.product);
            condition.signal();//通知消费者消费产品
            Thread.sleep(1000); //模拟延时效果
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

//模拟消费者
class Customer extends Thread{

    private ProductService productService;
    //构造方法
    public Customer(ProductService product){
        this.productService = product;
    }
    @Override
    public void run() {
        try {
            //不断从Product中获取产品进行消费
            while(true) {
               productService.getProduct();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//模拟生产者
class Producer extends Thread{
    private ProductService productService;
    //构造方法
    public Producer(ProductService product){
        this.productService = product;
    }
    @Override
    public void run() {
        try {
            //不断从Product中获取产品进行消费
            while(true) {
                productService.setProduct();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
程序运行结果:
生产者:生产了产品--产品23
消费者:消费了产品--产品23
生产者:生产了产品--产品46
消费者:消费了产品--产品46
生产者:生产了产品--产品23
消费者:消费了产品--产品23
生产者:生产了产品--产品96
消费者:消费了产品--产品96
生产者:生产了产品--产品49
消费者:消费了产品--产品49
生产者:生产了产品--产品37
消费者:消费了产品--产品37
生产者:生产了产品--产品88
消费者:消费了产品--产品88

3 使用SynchronousQueue实现

SynchronousQueue是一个不存储元素的的阻塞队列。SynchronousQueue中的每个put操作都必须等待一个take操作完成,否则不能继续向队列中添加元素。可以把它比喻成一个“快递员”,负责将生产者生产的产品直接递给消费者,非常适用于传递型场景,比如将一个线程的数据传递给另一个线程使用。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。具体使用代码如下:

/**
 * 使用SynchronousQueue实现一对一生产者消费者模式
 */
public class ThreadTest {
    public static void main(String[] args) {
        //创建用于存放产品的对象
        ProductService productService = new ProductService();
        //创建生产者线程对象
        Thread producer = new Producer(productService);
        //创建消费者线程对象
        Thread customer = new Customer(productService);

        //必须先开启生产者,再开启消费者
        producer.start();
        customer.start();
    }
}

//用字符串模拟产品
class ProductService{
    //创建用于存放产品的一个队列
    public SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
}

//模拟消费者
class Customer extends Thread{

    private ProductService productService;
    //构造方法
    public Customer(ProductService product){
        this.productService = product;
    }
    @Override
    public void run() {
        try {
            //不断从Product中获取产品进行消费
            while(true) {
                //从队列中取出产品
                String product = productService.synchronousQueue.take();
                System.out.println("消费者:消费了产品--" + product);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//模拟生产者
class Producer extends Thread{
    private ProductService productService;
    //构造方法
    public Producer(ProductService product){
        this.productService = product;
    }
    @Override
    public void run() {
        try {
            //不断从Product中获取产品进行消费
            while(true) {
                //生产产品放入队列中
                String product = "产品" + new Random().nextInt(100);
                productService.synchronousQueue.put(product);
                System.out.println("生产者:生产了产品--" + product);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
程序运行结果:
生产者:生产了产品--产品38
消费者:消费了产品--产品38
消费者:消费了产品--产品21
生产者:生产了产品--产品21
生产者:生产了产品--产品54
消费者:消费了产品--产品54
消费者:消费了产品--产品29
生产者:生产了产品--产品29
生产者:生产了产品--产品32
消费者:消费了产品--产品32
生产者:生产了产品--产品0
消费者:消费了产品--产品0

这个案例不必纠结最后生产者和消费者出现的先后顺序,因为代码是异步执行的,所以在执行到System.out.println()方法的时候,可能是消费者先抢到了锁,也可能是生产者先抢到了锁,但是生产者和消费者是成对出现的,而且在SynchronousQueue中,数据的put和take操作是成对存在的,是同步的,不会出现多线程问题。(补充:System.out.println方法源码中是sychronized修饰的方法,所以在同一时间,只能有一个线程使用该方法,所以这里才会出现生产者消费者顺序不一致问题,但是肯定是成对出现)。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值