线程通信问题--生产者和消费者问题

时间:2022-09-07 13:00:59

一、问题引入:首先实现一个线程通信的实例,使用两个线程交替打印输出100以内的数字。

代码实现如下:

 1 package com.baozi.exer;
 2 
 3 public class CommunicationTest {
 4     public static void main(String[] args) {
 5         Number number = new Number();
 6         Thread t1 = new Thread(number);
 7         Thread t2 = new Thread(number);
 8         t1.setName("线程1");
 9         t2.setName("线程2");
10         t1.start();
11         t2.start();
12     }
13 
14 }
15 
16 class Number implements Runnable {
17     private int number = 1;
18 
19     @Override
20     public void run() {
21         while (true) {
22             synchronized (this) {
23                 notify();
24                 if (number <= 100) {
25                     try {
26                         Thread.sleep(100);
27                     } catch (InterruptedException e) {
28                         e.printStackTrace();
29                     }
30                     System.out.println(Thread.currentThread().getName() + ":" + number);
31                     number++;
32                     try {
33                         wait();
34                     } catch (InterruptedException e) {
35                         e.printStackTrace();
36                     }
37                 } else {
38                     break;
39                 }
40             }
41         }
42     }
43 }

wait()、notify()、notifyAll()三个方法的介绍:

  • wait():某个对象调用wait()方法能让当前线程阻塞,并且当前线程还会释放所拥有的锁
  • 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
  • 调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

备注:wait()、notify()、notifyAll()三个方法都是定义在同步代码块或者是同步方法中的,并且都是同步代码块或者同步方法的同步监视器进行调用这些方法。

二、为什么这三个方法都是定义在Object类中,而不是定义在Thread类中?

  这三个方法都是定义在Object类中,因为我们知道,这三个方法都是存在于同步代码块或者同步方法中的,而且必须是该代码块的同步监视器进行调用这些方法,那么因为同步监视器是任何一个类的对象都可以充当,如果要保证任何一个类的对象都有这三个方法并且能进行调用,就只能把这三个方法方法所有类的父类--Object类中。

三、Sleep()方法和Wait()方法有什么异同?

相同点:一旦执行这两个方法,都会使当前线程进入阻塞状态

不同点:

  • 两个方法声明的位置不同:sleep()方法是在Thread类中声明为一个静态方法,而wait()方法是定义在Object类中
  • 两个方法的使用位置不同:sleep可以在任何位置使用,但是wait()方法只能在同步代码块或者是同步方法中使用
  • sleep()方法执行完之后虽然会使当前线程进入阻塞状态,但是并不会使当前线程释放所拥有的锁,但是wait()方法不但会让当前的线程进入阻塞状态,还会是当前的线程释放拥有的锁。

四、经典的线程通信问题:生产者和消费者问题

  1 package com.baozi.java2;
  2 
  3 /**
  4  * 线程通信的应用:生产者消费者问题
  5  * 分析:
  6  * 1、是否是多线程的问题:是多线程问题,至少要包含生产者线程和消费者线程
  7  * 2、是否有共享数据的问题?有共享数据:产品(这里只用了一个产品的个数进行表示)
  8  * 3、如何解决线程的安全问题:使用同步机制
  9  * 4、如何解决线程之间的通信问题?wait()和notify()/notifyAll()
 10  */
 11 //店员类
 12 class Clerk {
 13     private int productNumber = 0;
 14     //生产产品
 15     public synchronized void produceProductor() {
 16         if(productNumber<20){
 17             productNumber++;
 18             System.out.println(Thread.currentThread().getName()+":开始生产第"+productNumber+"个产品");
 19           //只要生产者生产出至少一个产品,就能唤醒消费者线程进行消费
 20             notify();
 21         }else{
 22             //当产品的个数大于20的时候,生产者线程就要进入阻塞状态,等待消费者线程消费产品之后在进行生产
 23             try {
 24                 wait();
 25             } catch (InterruptedException e) {
 26                 e.printStackTrace();
 27             }
 28         }
 29     }
 30     //消费产品
 31     public synchronized void consumerProduct() {
 32         if(productNumber>0){
 33             System.out.println(Thread.currentThread().getName()+":开始消费第"+productNumber+"个产品");
 34             productNumber--;
 35             //只要消费者线程进行了一次消费,就能唤醒生产者线程进行生产
 36             notify();
 37         }else{
 38             //当产品的个数小于等于0的时候,消费者线程就要进入阻塞状态,等待生产者生产出产品之后在进行消费
 39             try {
 40                 wait();
 41             } catch (InterruptedException e) {
 42                 e.printStackTrace();
 43             }
 44         }
 45     }
 46 }
 47 
 48 //生产者类
 49 class Productor extends Thread {
 50     private Clerk clerk;
 51 
 52     public Productor(Clerk clerk) {
 53         this.clerk = clerk;
 54     }
 55 
 56     @Override
 57     public void run() {
 58         System.out.println(Thread.currentThread().getName() + ":开始生产产品.....");
 59         while (true) {
 60             try {
 61                 Thread.sleep(100);
 62             } catch (InterruptedException e) {
 63                 e.printStackTrace();
 64             }
 65             clerk.produceProductor();
 66         }
 67     }
 68 }
 69 
 70 //消费者类
 71 class Consumer extends Thread {
 72     private Clerk clerk;
 73 
 74     public Consumer(Clerk clerk) {
 75         this.clerk = clerk;
 76     }
 77 
 78     @Override
 79     public void run() {
 80         System.out.println(Thread.currentThread().getName() + ":开始消费产品.....");
 81         while (true) {
 82             try {
 83                 Thread.sleep(100);
 84             } catch (InterruptedException e) {
 85                 e.printStackTrace();
 86             }
 87             clerk.consumerProduct();
 88         }
 89     }
 90 }
 91 //测试类,创建一个生产者生产商品,创建一个消费者消费产品
 92 public class ProductorTest {
 93     public static void main(String[] args) {
 94         Clerk clerk = new Clerk();
 95         Thread p1 = new Productor(clerk);
 96         p1.setName("生产者1");
 97         Thread c1 = new Consumer(clerk);
 98         c1.setName("消费者1");
 99         p1.start();
100         c1.start();
101     }
102 }