java多线程解决生产者与消费者问题

问题描述:生产者与消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个(多个)共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

生产者与消费者问题中其实隐含了两个问题:

  • 线程安全问题:因为生产者与消费者共享数据缓冲区,产生安全问题。不过这个问题可以使用同步解决。
  • 线程的协调工作问题:
    • 要解决该问题,就必须让生产者线程在缓冲区满时等待(wait),暂停进入阻塞状态,等到下次消费者消耗了缓冲区中的数据的时候,通知(notify)正在等待的线程恢复到就绪状态,重新开始往缓冲区添加数据。同样,也可以让消费者线程在缓冲区空时进入等待(wait),暂停进入阻塞状态,等到生产者往缓冲区添加数据之后,再通知(notify)正在等待的线程恢复到就绪状态。通过这样的通信机制来解决此类问题。

方式一:使用同步方法+线程通信(notify和wait方法)实现

public class ProducerConsumerDemo {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        ProducerThread producerThread = new ProducerThread(clerk);
        producerThread.setName("生产者A");
        CounsumberThread counsumberThread1 = new CounsumberThread(clerk);
        counsumberThread1.setName("消费者A");
        CounsumberThread counsumberThread2 = new CounsumberThread(clerk);
        counsumberThread2.setName("消费者B");

        //线程就绪
        producerThread.start();
        counsumberThread1.start();
        counsumberThread2.start();
    }
}

class Clerk {
    //当前产品数量,初始值为0
    public static int currentCount = 0;
    //最多产品数量
    public static final int MAX_COUNT = 20;
    //最少产品数量
    public static final int MIN_COUNT = 1;

    /**
     * 生成一个商品
     */
    public synchronized   void addAProduct() {
        //产品数量未到上限
        if (currentCount < MAX_COUNT) {
            //产品数量+1
            Clerk.currentCount++;
            System.out.println(LocalDateTime.now() + "-----" + Thread.currentThread().getName() + "生产了" + Clerk.currentCount + "号产品");
            //唤醒沉睡的消费者线程
            notifyAll();
        } else {
            try {
                //产品数量达到上限,生产者线程需要阻塞,等待消费者线程唤醒
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 消费1个商品
     */
    public synchronized   void subAProduct() {
        //产品数量不为0
        if (currentCount >= MIN_COUNT) {
            //产品数量-1
            System.out.println(LocalDateTime.now() + "-----" + Thread.currentThread().getName() + "消费了" + Clerk.currentCount + "号产品");
            Clerk.currentCount--;
            //唤醒沉睡的生产者线程
            notifyAll();

        } else {
            try {
                //产品数量为0,消费者线程需要阻塞,等待生产者线程唤醒
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

class ProducerThread extends Thread {

    private Clerk clerk;

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

    @Override
    public void run() {
        while (true) {
            try {
                //随机沉睡时间,范围[1-50]
                Thread.sleep((int) (Math.random() * 50) + 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            clerk.addAProduct();
        }
    }
}

class CounsumberThread extends Thread {
    private Clerk clerk;

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

    @Override
    public void run() {
        while (true) {
            try {
                //随机沉睡时间,范围[1-100]
                Thread.sleep((int) (Math.random() * 100) + 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            clerk.subAProduct();

        }
    }
}

补充:可以使用Lock锁来解决上述问题

public class ProducerConsumerDemo {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        ProducerThread producerThread = new ProducerThread(clerk);
        producerThread.setName("生产者A");
        CounsumberThread counsumberThread1 = new CounsumberThread(clerk);
        counsumberThread1.setName("消费者A");
        CounsumberThread counsumberThread2 = new CounsumberThread(clerk);
        counsumberThread2.setName("消费者B");

        //线程就绪
        producerThread.start();
        counsumberThread1.start();
        counsumberThread2.start();
    }
}

class Clerk {
    //当前产品数量,初始值为0
    public static int currentCount = 0;
    //最多产品数量
    public static final int MAX_COUNT = 20;
    //最少产品数量
    public static final int MIN_COUNT = 1;
	//利用Lock来控制对共享资源的访问
    ReentrantLock lock = new ReentrantLock();
    /**
     * 生成一个商品
     */
    public    void addAProduct() {

        try {
            lock.lock();
            //产品数量未到上限
            if (currentCount < MAX_COUNT) {
                //产品数量+1
                Clerk.currentCount++;
                System.out.println(LocalDateTime.now() + "-----" + Thread.currentThread().getName() + "生产了" + Clerk.currentCount + "号产品");
            } 
        }finally {
            lock.unlock();
        }
    }

    /**
     * 消费1个商品
     */
    public    void subAProduct() {
        try {
            lock.lock();
            //产品数量不为0
            if (currentCount >= MIN_COUNT) {
                //产品数量-1
                System.out.println(LocalDateTime.now() + "-----" + Thread.currentThread().getName() + "消费了" + Clerk.currentCount + "号产品");
                Clerk.currentCount--;
            } 
        }finally {
            lock.unlock();
        }
    }
}

class ProducerThread extends Thread {

    private Clerk clerk;

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

    @Override
    public void run() {
        while (true) {
            try {
                //随机沉睡时间,范围[1-50]
                Thread.sleep((int) (Math.random() * 50) + 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            clerk.addAProduct();
        }
    }
}

class CounsumberThread extends Thread {
    private Clerk clerk;

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

    @Override
    public void run() {
        while (true) {
            try {
                //随机沉睡时间,范围[1-100]
                Thread.sleep((int) (Math.random() * 100) + 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            clerk.subAProduct();

        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值