上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者模式是一个经典的线程同步以及通信的模型。
假设有这样一种情况,有一个篮子,篮子里只能放一个鸡蛋,A线程专门往篮子里放鸡蛋,如果篮子里有鸡蛋,则一直等到篮子里没鸡蛋,B线程专门从篮子里取鸡蛋,如果篮子里没鸡蛋,则一直等到篮子里有鸡蛋。这里篮子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放 鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当篮子里有鸡蛋时,A线程阻塞,B线程就绪,篮子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:
/** * */ package com.wsheng.thread.communication; /** * 鸡蛋类 * * @author Wang Sheng(Josh) * */ public class Egg { private double weight; private double price; public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
/** * */ package com.wsheng.thread.communication; import java.util.ArrayList; import java.util.List; /** * 线程间的通信: 放鸡蛋和取鸡蛋 - 生产者和消费者 * @author Wang Sheng(Josh) * */ public class Basket { /** 共享资源:篮子 */ private List<Egg> eggs = new ArrayList<Egg>(); /** 取鸡蛋*/ public synchronized Egg getEgg() { while (eggs.size() == 0) { try { wait(); // 当前线程进入阻塞队列 } catch (InterruptedException e) { e.printStackTrace(); } } Egg egg = eggs.get(0); // 清空篮子 eggs.clear(); // 唤醒阻塞队列的某线程到就绪队列 notify(); System.out.println("拿到鸡蛋"); return egg; } /** 放鸡蛋 */ public synchronized void putEgg(Egg egg) { while (eggs.size() > 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } eggs.add(egg); // 唤醒阻塞队列的某线程到就绪队列 notify(); System.out.println("放入鸡蛋"); } static class PutThread extends Thread { private Basket basket; private Egg egg = new Egg(); public PutThread(Basket basket) { this.basket = basket; } public void run() { basket.putEgg(egg); } } static class GetThread extends Thread { private Basket basket; public GetThread(Basket basket) { this.basket = basket; } public void run() { basket.getEgg(); } } public static void main(String[] args) { Basket basket = new Basket(); for (int i = 0; i < 10; i++) { new PutThread(basket).start(); new GetThread(basket).start(); } } }
输出结果:
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
放入鸡蛋
拿到鸡蛋
程序开始,A线程判断篮子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,篮子非空,执行等待,这 个A线程进入阻塞队列;然后一个B线程执行,篮子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放 入鸡蛋;如果再来A线程重复第一步,再来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。
这个例子中,同步(互斥)的资源是一个实实在在的篮子对象,其实,共享资源也可以是一个简单的变量,如下面的例子,共享资源是一个bool变量。
例: 子线程循环10次,然后主线程循环100次,如此循环100次。子循环执行时主循环不能执行,主循环执行时子循环也不能执行。
/** * */ package com.wsheng.thread.synchronize; /** * @author Wang Sheng(Josh) * */ public class LoopThreadTest { public static void main(String[] args) { final Loop loopObj = new Loop(); new Thread(new Runnable() { public void run() { execute(loopObj, "sub"); } }).start(); execute(loopObj, "main"); } public static void execute(Loop loop, String threadType) { for (int i = 0; i < 100; i++) { try { if ("main".equals(threadType)) { loop.mainThread(i); } else { loop.subThread(i); } } catch (InterruptedException e) { e.printStackTrace(); } } } } class Loop { private boolean isSubThread = true; public synchronized void mainThread(int loop) throws InterruptedException { while (isSubThread) { this.wait(); } for (int i = 0; i < 100; i++) { System.out.println("main thread sequence of " + i + ", loop of " + loop); } isSubThread = true; // 主线程执行完毕,由子线程来执行 this.notify(); // 唤醒阻塞队列中的子线程到就绪队列 } public synchronized void subThread(int loop) throws InterruptedException { while (!isSubThread) { this.wait(); } for (int i = 0; i < 10; i++) { System.out.println("sub thread sequence of " + i + ", loop of " + loop); } isSubThread = false; // 子线程执行完毕,由主线程来执行 this.notify(); // 唤醒阻塞队列中的主线程到就绪队列 } }
需要注意的是,在上面的例子中,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥。
相关推荐
在并发编程中,"生产者-消费者"模式是一种经典的解决问题的范式,用于协调两个或更多线程间的协作,其中一部分线程(生产者)生成数据,另一部分线程(消费者)消费这些数据。 生产者-消费者模型的核心在于共享资源...
### Java多线程—线程间的通信 #### 一、线程间的通信 ##### (1)为什么要处理线程间的通信? 在多线程环境中,不同的线程可能需要协同工作来完成一项任务。例如,一个线程负责生产数据,另一个线程负责消费这些...
在Java编程中,线程间的通信是多线程编程中的一个重要概念,特别是在处理并发和协作任务时。生产者消费者模型是一种经典的线程同步问题,它模拟了实际生活中的生产过程和消费过程,使得生产者线程可以将数据生产出来...
生产者消费者模式是一种经典的多线程同步问题解决方案,它源于现实世界中的生产流水线,用于描述生产者(Producer)和消费者(Consumer)之间的协作关系。在这个模式中,生产者负责生成产品并放入仓库,而消费者则从...
总结来说,“java多线程(生产者与消费者)”是关于如何在Java中使用同步、线程间通信和共享资源来实现高效且安全的并发编程。通过理解并熟练应用这些概念和工具,开发者可以构建出能够充分利用多核处理器能力的高...
总之,多生产者-多消费者模式是并发编程中的重要设计模式,通过合理利用同步机制,能够有效地解决线程间的通信问题,提高系统的并发性能。在Java中,我们可以借助`BlockingQueue`等工具实现这一模式,从而构建高效、...
生产者与消费者模式是设计模式中的经典范例,它有效地展示了线程间的协作和同步。这个模式主要解决的问题是数据的生产和消费过程中的等待与协作问题。 在多线程环境下,生产者负责生成数据,而消费者则负责处理这些...
- **生产者消费者模式**:使用`BlockingQueue`实现线程间的协同工作。 - **单例模式**:考虑多线程环境下的线程安全单例实现,如双重检查锁定。 - **读写锁**:`ReentrantReadWriteLock`提供读写分离的锁机制,...
Java多线程生产者消费者模型是一种典型的线程协作模式,用于解决并发编程中资源的高效利用和同步问题。在这个模型中,"生产者"线程负责生成数据,而"消费者"线程则负责处理这些数据。为了实现这种模式,Java提供了...
Java多线程设计模式是Java开发中的核心概念,它涉及到如何高效、安全地在多个执行线程之间共享资源和协调任务。设计模式是解决特定问题的成熟方案,它们是编程经验的结晶,可以帮助开发者在面临多线程挑战时快速找到...
"生产者-消费者"模式是多线程编程中的一个经典设计模式,它体现了线程间的协作和同步。在这个模式中,"生产者"线程负责创建资源,而"消费者"线程则负责消耗这些资源。这个模式在实际应用中非常常见,例如在消息队列...
### Java多线程知识点总结及企业真题解析 #### 一、知识点总结 ##### (1)多线程相关概念 1. **程序、进程和线程的区分**: - **程序**:为了完成特定的任务而编写的指令集合。它是静态的概念。 - **进程**:...
在Java编程中,生产者消费者模式是一种典型的多线程协作模型,用于解决系统资源供需不平衡的问题。这个模式的核心思想是将生产数据和消费数据的过程解耦,使得生产者可以独立地生产数据,而消费者可以独立地消费数据...
- 生产者-消费者模式:使用`BlockingQueue`实现线程间的生产与消费。 - 线程池模式:通过`ExecutorService`实现线程复用,避免频繁创建销毁线程。 - 单例模式在多线程环境下的应用:确保类只有一个实例,通常使用...
总的来说,理解和掌握生产者消费者问题及其解决策略是Java多线程编程的重要部分,这不仅有助于编写高效、可靠的并发代码,也是提升软件设计能力的关键一步。通过不断实践和学习,开发者能够更好地应对复杂的并发场景...
生产者消费者模型是一种常见的线程间协作模式,通过BlockingQueue实现,生产者线程向队列中添加元素,消费者线程从队列中取出元素。还有读者写者模式,允许多个读者同时读取资源,但只允许一个写者进行修改,可以...
"Java多线程通信机制研究" Java多线程通信机制是Java程序设计中的一个复杂技术,涉及到...Java多线程通信机制是一个复杂的技术,需要深入了解Java多线程编程和通信机制的原理和实现方法,才能更好地应用于实际项目中。
这些案例涵盖了从简单的并发任务到复杂的多线程设计模式,如生产者消费者模型、线程池的使用以及线程间的通信技巧。通过这些实战练习,读者能够更好地理解多线程编程中的挑战和解决方案。 本书还涉及到了Java并发...
"典型的多线程--生产和消费"这个主题,通常指的是生产者-消费者问题,这是一种经典的设计模式,用于解决如何在一个数据结构(如队列)中高效地存储和检索数据,同时避免生产者过于快速地生成数据而消费者无法及时...
3. 生产者-消费者模式:使用`BlockingQueue`实现线程间的协作。 4. 状态机模式:表示线程的不同状态,如`Runnable`、`Blocked`、`Terminated`等。 5. 负载均衡模式:通过负载均衡器分配任务给线程,如`...