Java 线程 案例:生产者与消费者

简介: Java 线程 案例:生产者与消费者

一  案例目地

掌握控制线程的执行次序,实现按照要求的线程执行

二  案例要求:

生产者:

  1. 判断 “桌子上” 是否有 “食品” ,如果有就等待,如果没有才生产
  2. 把 “食品”  放在 “桌子上”。
  3. 叫醒等待的消费者
  4. 生产者存在生产数量的限制  

消费者:

  1. 判断 “桌子上” 是否有 “食品” ,如果没有就等待,如果有就 “吃” 掉 “食品”
  2. 吃完后,叫醒等待的生产者,继续生产 “食品”

三  方法提供

  1.  
1  void wait(); 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或者 notifyAll()方法
2  void notify(); 唤醒正在等待对象监视器的单个线程
3  void notifyAll(); 唤醒正在等待对象监视器的所有线程
  1. 注:使用什么对象作为锁,那么就必须用这个对象去调用等待和唤醒方法,notify()随机唤醒这把锁上等待的一个线程,notifyAll()唤醒这把锁上的所有线程

四  代码演示

  1. 测试类:
public class Test {
    public static void main(String[] args) {
        Producer p = new Producer();
        Consumer c = new Consumer();
        p.start();
        c.start();
    }
}
  1. 生产者类:
//创建生产者类
public class Producer extends Thread{
    public void run() {
        //因为操作不止一次,使用while循环
        while (true){
            //共享数据,使用同步代码块
            synchronized (Desk.lock){
                //判断是否达到,生产者上线
                if (Desk.count == 0){
                    break;
                }else {
                    //判断桌上是否有“食品”
                    if(Desk.flag){
                        //如果有,就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        //如果没有,进行生产
                        System.out.println("生产者正在生产");
                        //表示有食品了
                        Desk.flag = true;
                        //叫醒消费者
                        Desk.lock.notifyAll();
                        //生产者上线数减一
                        Desk.count--;
                    }
                }
            }
        }
    }
}
  1. 消费者类:
//创建消费者类
public class Consumer extends Thread{
    public void run() {
        //因为操作不止一次,使用while循环
        while (true){
            //共享数据,使用同步代码块
            synchronized (Desk.lock){
                //判断是否达到,生产者上线
                if (Desk.count == 0){
                    break;
                }else {
                    //判断桌上是否有“食品”
                    if(Desk.flag){
                        //如果有
                        System.out.println("消费者正在吃掉食品");
                        //表示没有食品了
                        Desk.flag = false;
                        //叫醒生产者
                        Desk.lock.notifyAll();
                    }else {
                        //如果没有,进行等待
                        //使用什么对象作为锁,那么就必须用这个对象去调用等待与唤醒方法
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
  1. “桌子类”:
//创建桌子类
public class Desk {
    //定义标记,如果为true,表示桌子上有“食品”,如果false表示无
    public static boolean flag = false;
    //定义,生产的生产上线
    public static int count = 10;
    //锁对象,在此次定义,使用final,目地:保证消费者与生产者锁对象唯一
    public static final Object lock = new Object();
}
  1. 效果演示:实现了,对生产者与消费者的执行次序控制image.png

五  线程写法小结

  1. while(true)死循环
  2. synchronized  锁,锁对象要唯一
  3. 判断,共享数据是否结束,如果结束就跳出循环
  4. 如果没有结束,执行题目逻辑要求

六  代码优化

  为了体现面向对象编程的特性,对代码进行优化

  1. 桌子类进行封装:
//创建桌子类
public class Desk {
    //定义标记,如果为true,表示桌子上有“食品”,如果false表示无
    //public static boolean flag = false;
    private boolean flag = false;
    //定义,生产的生产上线
    //public static int count = 10;
    private int count = 10;
    //锁对象,在此次定义,使用final,目地:保证消费者与生产者锁对象唯一
    //public static final Object lock = new Object();
    private final Object lock = new Object();
    //空参构造
    public Desk() {
    }
    //全参构造
    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }
    //提供get,set方法
    //boolean的get方法叫做is...
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    //lock因为由final修饰,所以没有set方法
    public Object getLock() {
        return lock;
    }
    //toString()方法重写
    public String toString() {
        return "Desk{" +
                "flag=" + flag +
                ", count=" + count +
                ", lock=" + lock +
                '}';
    }
}
  1. 测试类:
public class Test {
    public static void main(String[] args) {
        //在测试类中,创建esk对象D,防止Producer与Consumer的共享数据不同
        Desk desk = new Desk();
        //提供分别的Desk对象构造方法
        Producer p = new Producer(desk);
        Consumer c = new Consumer(desk);
        p.start();
        c.start();
    }
}
  1. 生产者类:
//创建生产者类
public class Producer extends Thread{
    private Desk desk;
    public Producer(Desk desk) {
        this.desk = desk;
    }
    public void run() {
        //因为操作不止一次,使用while循环
        while (true){
            //共享数据,使用同步代码块
            synchronized (desk.getLock()){
                //判断是否达到,生产者上线
                if (desk.getCount() == 0){
                    break;
                }else {
                    //判断桌上是否有“食品”
                    if(desk.isFlag()){
                        //如果有,就等待
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        //如果没有,进行生产
                        System.out.println("生产者正在生产");
                        //表示有食品了
                        desk.setFlag(true);
                        //叫醒消费者
                        desk.getLock().notifyAll();
                        //生产者上线数减一
                        desk.setCount(desk.getCount() - 1);
                    }
                }
            }
        }
    }
}
  1. 消费者类:
//创建消费者类
public class Consumer extends Thread{
    private Desk desk;
    public Consumer(Desk desk) {
        this.desk = desk;
    }
    public void run() {
        //因为操作不止一次,使用while循环
        while (true){
            //共享数据,使用同步代码块
            synchronized (desk.getLock()){
                //判断是否达到,生产者上线
                if (desk.getCount() == 0){
                    break;
                }else {
                    //判断桌上是否有“食品”
                    if(desk.isFlag()){
                        //如果有
                        System.out.println("消费者正在吃掉食品");
                        //表示没有食品了
                        desk.setFlag(false);
                        //叫醒生产者
                        desk.getLock().notifyAll();
                    }else {
                        //如果没有,进行等待
                        //使用什么对象作为锁,那么就必须用这个对象去调用等待与唤醒方法
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}


目录
相关文章
|
1天前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
9 3
|
1天前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
15 2
|
1天前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
11 3
|
1天前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第11天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个方面,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。我们将通过实例和代码片段来说明这些概念和技术。
2 0
|
1天前
|
Java 调度
Java并发编程:深入理解线程池
【5月更文挑战第11天】本文将深入探讨Java中的线程池,包括其基本概念、工作原理以及如何使用。我们将通过实例来解释线程池的优点,如提高性能和资源利用率,以及如何避免常见的并发问题。我们还将讨论Java中线程池的实现,包括Executor框架和ThreadPoolExecutor类,并展示如何创建和管理线程池。最后,我们将讨论线程池的一些高级特性,如任务调度、线程优先级和异常处理。
|
2天前
|
Java
线程安全问题-卖票案例实现
线程安全问题-卖票案例实现
5 0
|
2天前
|
安全 Java
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
【JAVA进阶篇教学】第十篇:Java中线程安全、锁讲解
|
2天前
|
安全 Java
【JAVA进阶篇教学】第六篇:Java线程中状态
【JAVA进阶篇教学】第六篇:Java线程中状态
|
2天前
|
缓存 Java
【JAVA进阶篇教学】第五篇:Java多线程编程
【JAVA进阶篇教学】第五篇:Java多线程编程
|
2天前
|
Java
【JAVA基础篇教学】第十二篇:Java中多线程编程
【JAVA基础篇教学】第十二篇:Java中多线程编程