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

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

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

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

       由简单开始,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()是把线程池中的所有等待线程都唤醒。


相关文章
|
5天前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
6天前
|
安全 Java
Java模拟生产者-消费者问题。生产者不断的往仓库中存放产品,消费者从仓库中消费产品。其中生产者和消费者都可以有若干个。在这里,生产者是一个线程,消费者是一个线程。仓库容量有限,只有库满时生产者不能存
该博客文章通过Java代码示例演示了生产者-消费者问题,其中生产者在仓库未满时生产产品,消费者在仓库有产品时消费产品,通过同步机制确保多线程环境下的线程安全和有效通信。
|
6天前
|
缓存 监控 Java
Java性能优化:从单线程执行到线程池管理的进阶实践
在Java开发中,随着应用规模的不断扩大和用户量的持续增长,性能优化成为了一个不可忽视的重要课题。特别是在处理大量并发请求或执行耗时任务时,单线程执行模式往往难以满足需求,这时线程池的概念便应运而生。本文将从应用场景举例出发,探讨Java线程池的使用,并通过具体案例和核心代码展示其在实际问题解决中的强大作用。
22 1
|
9天前
|
消息中间件 安全 Kafka
"深入实践Kafka多线程Consumer:案例分析、实现方式、优缺点及高效数据处理策略"
【8月更文挑战第10天】Apache Kafka是一款高性能的分布式流处理平台,以高吞吐量和可扩展性著称。为提升数据处理效率,常采用多线程消费Kafka数据。本文通过电商订单系统的案例,探讨了多线程Consumer的实现方法及其利弊,并提供示例代码。案例展示了如何通过并行处理加快订单数据的处理速度,确保数据正确性和顺序性的同时最大化资源利用。多线程Consumer有两种主要模式:每线程一个实例和单实例多worker线程。前者简单易行但资源消耗较大;后者虽能解耦消息获取与处理,却增加了系统复杂度。通过合理设计,多线程Consumer能够有效支持高并发数据处理需求。
28 4
|
26天前
|
监控 Java 开发者
深入理解Java并发编程:线程池的原理与实践
【5月更文挑战第85天】 在现代Java应用开发中,高效地处理并发任务是提升性能和响应能力的关键。线程池作为一种管理线程的机制,其合理使用能够显著减少资源消耗并优化系统吞吐量。本文将详细探讨线程池的核心原理,包括其内部工作机制、优势以及如何在Java中正确实现和使用线程池。通过理论分析和实例演示,我们将揭示线程池对提升Java应用性能的重要性,并给出实践中的最佳策略。
|
3天前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践
|
1月前
|
算法 Java 开发者
Java中的多线程编程技巧与实践
在现代软件开发中,多线程编程成为提升应用程序性能和响应能力的关键技术之一。本文将深入探讨Java语言中多线程编程的基础概念、常见问题及其解决方案,帮助开发者更好地理解和应用多线程技术。 【7月更文挑战第12天】
20 0
|
1月前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
36 0
|
1月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
40 0
|
1月前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
31 0