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();
                        }
                    }
                }
            }
        }
    }
}


目录
相关文章
|
6天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2天前
|
Java
深入理解Java中的多线程编程
本文将探讨Java多线程编程的核心概念和技术,包括线程的创建与管理、同步机制以及并发工具类的应用。我们将通过实例分析,帮助读者更好地理解和应用Java多线程编程,提高程序的性能和响应能力。
15 4
|
11天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
2天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
12天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
11天前
|
算法 Java 数据处理
Java并发编程:解锁多线程的力量
在Java的世界里,掌握并发编程是提升应用性能和响应能力的关键。本文将深入浅出地探讨如何利用Java的多线程特性来优化程序执行效率,从基础的线程创建到高级的并发工具类使用,带领读者一步步解锁Java并发编程的奥秘。你将学习到如何避免常见的并发陷阱,并实际应用这些知识来解决现实世界的问题。让我们一起开启高效编码的旅程吧!
|
13天前
|
Java 开发者
Java中的多线程编程基础与实战
【9月更文挑战第6天】本文将通过深入浅出的方式,带领读者了解并掌握Java中的多线程编程。我们将从基础概念出发,逐步深入到代码实践,最后探讨多线程在实际应用中的优势和注意事项。无论你是初学者还是有一定经验的开发者,这篇文章都能让你对Java多线程有更全面的认识。
17 1
|
11天前
|
安全 Java UED
Java并发编程:解锁多线程的潜力
在Java的世界里,并发编程如同一场精心编排的交响乐,每个线程扮演着不同的乐手,共同奏响性能与效率的和声。本文将引导你走进Java并发编程的大门,探索如何在多核处理器上优雅地舞动多线程,从而提升应用的性能和响应性。我们将从基础概念出发,逐步深入到高级技巧,让你的代码在并行处理的海洋中乘风破浪。