多线程实践-生产者消费者

简介: 多线程实践-生产者消费者

       当多个线程操作同一个资源,但是操作的动作不同时,就会需要线程间进行通信。很著名的是生产者消费者的例子。

       有一个盘子,只能放一片面包,生产者生产面包放入盘子,消费者从盘子中取走面包吃掉。

       由简单开始,i+1。先看一个生产者、一个消费者。

代码如下:

1. public class ProducerConsumerDemo {
2. public static void main(String[] args){
3. Resource r = new Resource();
4. 
5. new Thread(new Producer(r)).start();
6. new Thread(new Consumer(r)).start();
7.     }
8. }
9. 
10. class Resource{  //公共资源
11. private String name;
12. private int count =1;
13. private boolean flag = false;
14. 
15. public synchronized void set(String name){
16. if(flag)
17. try{this.wait();}catch(Exception e){}
18. this.name = name + "--" + count++;
19.         System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
20.         flag = true;
21.         notify();
22.     }
23. public synchronized void out(){
24. if(!flag)
25. try{this.wait();}catch(Exception e){}
26.         System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
27.         flag = false;
28.         notify();
29.     }
30. }
31. class Producer implements Runnable{
32. private Resource res;
33.     Producer(Resource res){
34. this.res = res;
35.     }
36. public void run(){
37. while(true){
38.             res.set("面包");
39.         }
40.     }
41. }
42. class Consumer implements Runnable{
43. private Resource res;
44.     Consumer(Resource res){
45. this.res = res;
46.     }
47. public void run(){
48. while(true){
49.             res.out();
50.         }
51.     }
52. }

       运行结果如图:

       由运行结果可以看到。Thread-0和Tread-1两个线程是交替进行,生产者生产商品i,消费者就把商品i消费掉。然后生产者生产商品i+1,消费者再消费商品i+1。

       本来我自己想的实现过程是这样的:对于盘子来说,它有两种状态,可以往里放面包和不可以放面包。生产者来了,如果可放,就生产一个面包,并把盘子置为不可放面包的状态,如果不可放,就什么都不操作。消费者来了,如果可放(就代表不能取),就什么都不操作,如果不可放(代表能取),就取走一个面包,并把盘子置为可放面包状态。代码如下:

1. class Resource{  //公共资源
2. private String name;
3. private int count =1;
4. private boolean flag = false;
5. 
6. public synchronized void set(String name){
7. if(flag)
8.         {
9.         }
10. //try{this.wait();}catch(Exception e){}
11. else{
12. this.name = name + "--" + count++;
13.             System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
14.             flag = true;
15.         }
16. //notify();
17.     }
18. public synchronized void out(){
19. if(!flag){
20.         }
21. //try{this.wait();}catch(Exception e){}
22. else{
23.             System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
24.             flag = false;
25.         }
26. 
27. //notify();
28.     }
29. }

       与上面的示例中代码的区别是没有使用wait()和notify()方法。一样能实现效果。看图:


       不用使用wait()和notify()与使用有什么区别呢?既然它存在,肯定是有它的道理的。猜测它的优点是效率更高。用什么方法可以验证一下?尝试着加了运行时间和打印输出。如图:

       (1)不用wait()/notify()

       (2)用wait()/notify()

       count为10000时,不使用wait()和notify()生产9999个面包需要1330ms,而使用wait()和notify()生产9999个面包只需要406ms。多次执行,每次的结果相差不大。

增加一下数量级,再比比,也很明显:6704msVS 3208ms。

       

(1)不用wait()/notify()(2)用wait()/notify()                  



       计时代码增加到了main方法所在类和公共资源类下,代码如下:

1. public class ProducerConsumerDemo {
2. public static long strateTime;
3. public static void main(String[] args){
4. Resource r = new Resource();
5.         strateTime = System.currentTimeMillis();
6. new Thread(new Producer(r)).start();
7. new Thread(new Consumer(r)).start();
8.     }
9. }
10. 
11. class Resource{  //公共资源
12. public static long endTime;
13. private String name;
14. private int count =1;
15. private boolean flag = false;
16. 
17. public synchronized void set(String name){
18. if(flag)
19. //        {
20. //           System.out.println("无效执行");
21. //        }
22. try{this.wait();}catch(Exception e){}
23. else{
24. this.name = name + "--" + count++;
25. if(count==100000){
26.                 endTime= System.currentTimeMillis();
27.                 System.out.println("程序运行时间:" + ( endTime - ProducerConsumerDemo.strateTime )+"ms");
28.             }
29.             System.out.println(Thread.currentThread().getName() + "...生产者:生产了"+this.name);
30.             flag = true;
31.         }
32.         notify();
33.     }
34. public synchronized void out(){
35. if(!flag)
36. //        {
37. //            System.out.println("无效执行");
38. //        }
39. try{this.wait();}catch(Exception e){}
40. else{
41.             System.out.println(Thread.currentThread().getName() + "...消费者:消费了"+this.name);
42.             flag = false;
43.         }
44. 
45.         notify();
46.     }
47. }

       对比发现,差不多两倍的差距,效率是不一样的。是因为啥呢?

       //TODO:

     

多线程方法汇总:

       等待唤醒:wait()、notify()、notifyAll()。

       waite()是把线程由运行状态置为等待状态,等待线程都存在线程池中。

       notify()方法是把等待状态的线程从线程池中唤醒。通常唤醒线程池中的第一个等待的线程。

       notifyAll()是把线程池中的所有等待线程都唤醒。


相关文章
|
12天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
172 6
|
2月前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
71 1
|
2月前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
|
2月前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
2月前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
2月前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
2月前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
31 3
|
2月前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
21 2