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

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

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

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

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


相关文章
|
2天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
2天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
12天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
19 3
|
14天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
12天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
12 2
|
13天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。
|
17天前
|
Java API 调度
Java中的多线程编程:理解与实践
本文旨在为读者提供对Java多线程编程的深入理解,包括其基本概念、实现方式以及常见问题的解决方案。通过阅读本文,读者将能够掌握Java多线程编程的核心知识,提高自己在并发编程方面的技能。
|
17天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
13 1
|
24天前
|
安全 Java UED
Java中的多线程编程:从基础到实践
本文深入探讨了Java中的多线程编程,包括线程的创建、生命周期管理以及同步机制。通过实例展示了如何使用Thread类和Runnable接口来创建线程,讨论了线程安全问题及解决策略,如使用synchronized关键字和ReentrantLock类。文章还涵盖了线程间通信的方式,包括wait()、notify()和notifyAll()方法,以及如何避免死锁。此外,还介绍了高级并发工具如CountDownLatch和CyclicBarrier的使用方法。通过综合运用这些技术,可以有效提高多线程程序的性能和可靠性。
|
23天前
|
缓存 Java UED
Java中的多线程编程:从基础到实践
【10月更文挑战第13天】 Java作为一门跨平台的编程语言,其强大的多线程能力一直是其核心优势之一。本文将从最基础的概念讲起,逐步深入探讨Java多线程的实现方式及其应用场景,通过实例讲解帮助读者更好地理解和应用这一技术。
35 3