一、问题引入:首先实现一个线程通信的实例,使用两个线程交替打印输出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 }