线程通信的应用:经典例题:生产者/消费者问题

    生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

    这里可能出现两个问题:
    生产者比消费者快时,消费者会漏掉一些数据没有取到。
    消费者比生产者快时,消费者会取相同的数据。

    分析:
    1. 是否是多线程问题?是,生产者线程,消费者线程
    2. 是否有共享数据?是,店员(或产品)
    3. 如何解决线程的安全问题?同步机制,有三种方法
    4. 是否涉及线程的通信?是

    1. class Clerk{//售货员
    2. private int productCount = 0;
    3. //生产产品
    4. public synchronized void produceProduct() {
    5. if(productCount < 20){
    6. productCount++;
    7. System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
    8. notify();//已生产,唤醒消费者
    9. }else{
    10. //产品已满,等待消费
    11. try {
    12. wait();//this.wait()
    13. } catch (InterruptedException e) {
    14. e.printStackTrace();
    15. }
    16. }
    17. }
    18. //消费产品
    19. public synchronized void consumeProduct() {
    20. if(productCount > 0){
    21. System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
    22. productCount--;
    23. notify();//已消费,唤醒生产者
    24. }else{
    25. //没有产品可消费,等待生产
    26. try {
    27. wait();
    28. } catch (InterruptedException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }
    33. }
    34. class Producer extends Thread{//生产者
    35. private Clerk clerk;
    36. public Producer(Clerk clerk) {
    37. this.clerk = clerk;
    38. }
    39. @Override
    40. public void run() {
    41. System.out.println(getName() + ":开始生产产品.....");
    42. while(true){
    43. try {//减缓生产速度
    44. Thread.sleep(10);
    45. } catch (InterruptedException e) {
    46. e.printStackTrace();
    47. }
    48. clerk.produceProduct();
    49. }
    50. }
    51. }
    52. class Consumer extends Thread{//消费者
    53. private Clerk clerk;
    54. public Consumer(Clerk clerk) {
    55. this.clerk = clerk;
    56. }
    57. @Override
    58. public void run() {
    59. System.out.println(getName() + ":开始消费产品.....");
    60. while(true){
    61. try {
    62. Thread.sleep(20);
    63. } catch (InterruptedException e) {
    64. e.printStackTrace();
    65. }
    66. clerk.consumeProduct();
    67. }
    68. }
    69. }
    70. public class ProductTest {
    71. public static void main(String[] args) {
    72. Clerk clerk = new Clerk();
    73. Producer p1 = new Producer(clerk);
    74. p1.setName("生产者1");
    75. Consumer c1 = new Consumer(clerk);
    76. c1.setName("消费者1");
    77. Consumer c2 = new Consumer(clerk);
    78. c2.setName("消费者2");
    79. p1.start();
    80. c1.start();
    81. c2.start();
    82. }
    83. }