当多个线程操作同一个资源,但是操作的动作不同时,就会需要线程间进行通信。很著名的是生产者消费者的例子。
有一个盘子,只能放一片面包,生产者生产面包放入盘子,消费者从盘子中取走面包吃掉。
由简单开始,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()是把线程池中的所有等待线程都唤醒。