Java多线程学习-----wait()、notify/notifyAll()的使用

匿名 (未验证) 提交于 2019-12-02 21:45:52

多线程的几种状态

新建就绪运行阻塞死亡

  • 新建:
    线程被创建出来
  • 就绪:
    具有CPU的执行资格, 但是不具有CPU的执行权
  • 运行:
    具有CPU的执行资格, 也具有CPU的执行权
  • 阻塞:
    不具有CPU的执行资格, 也不具有CPU的执行权
  • 死亡:
    不具有CPU的执行资格, 也不具有CPU的执行权
线程的阻塞状态

上一篇有说过sleep可以使线程处于阻塞状态,今天要说的 wait() 方法也可以使线程处于阻塞状态。首先来看一下这两个方法的区别;

  • 共同点:
    wait()方法和sleep()方法 都可以使线程处于阻塞状态
    都可以设置线程的阻塞时间

  • 不同点:
    wait() 可以设置时间量,也可以不设置。
    sleep() 是要必须要设置休眠的时间
    wait() 必须先获得锁,一般配合synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法;sleep()不一定要获得锁。
    sleep() 休眠不会释放锁
    wait() 一旦等待就会释放锁,让出cpu的执行权,直到当 notify/notifyAll() 被执行时候,才会被唤醒然后继续往下执行

wait() 阻塞状态的唤醒

当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

1. notify/notifyAll() 这两种方法的区别
notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。
notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。比如在生产者-消费者里面的使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。

2. notify/notifyAll() 执行了之后是立刻就释放锁吗?
不是的;当 notify/notifyAll() 被执行之后,就会唤醒对应所对象的一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,才会再次释放锁。
也就是说,notify/notifyAll() 的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程 。

3. 使用wait()和notify/notifyAll()实现生产者和消费者问题

  • 生产者和消费者问题是什么?
    首先来举一个例子,你去大郎烧饼店买包子吃,大郎说包子没有了你先等一会儿,我这就让金莲给你做两个来,你等了五分钟烧饼出炉了,你终于吃到了烧饼。
    生产者和消费者模型基本原理也是这样,生产者产生消费者所需要的资源,并存在缓冲区;消费者消耗缓冲区中的资源,前提是缓冲区中有所需要的资源,如果缓冲区中没有需要的资源,那么消费者就停止处于等待状态,并通知生产者生产资源;
  • 代码演示
    现有一个学生类,该类的属性包括姓名和年龄;需求:由生产者设置学生对象的属性,消费者打印出生产者所设置的学生对象的姓名和年龄
  1. 测试程序
public class MyTest {     public static void main(String[] args) {                 Student student = new Student();         //生产者线程         SetThread setThread = new SetThread(student);         //消费者线程         GetThread getThread = new GetThread(student);         getThread.start();         setThread.start();     } } 
  1. 学生类
public class Student {     public String name;     public int age;     //设置一个标记     public boolean flag = false;  //默认值false 代表没有资源,true 代表有资源 } 
  1. 生产者:
public class SetThread extends Thread {     Student student;     int i = 0;      public SetThread(Student student) {         this.student = student;     }      @Override     public void run() {         while (true) {             synchronized (student) {                 if (student.flag) {                     //作为生产者,如果有资源了                     try {                         student.wait(); //等着                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }                 //在生产资源                 if (i % 2 == 0) {                     student.name = "张三";                     student.age = 23;                 } else {                     student.name = "李四";                     student.age = 24;                 }                 //修改标记                 student.flag = true;                 student.notify();//通知消费者去消费,唤醒之后,两个线程还得抢时间片                 i++;             }          }     } }  
  1. 消费者
public class GetThread extends Thread {     Student student;      public GetThread(Student student) {         this.student = student;     }      @Override     public void run() {         while (true) {             synchronized (student) {                 if (!student.flag) {                     //没有资源作为消费者,等着                     try {                         student.wait(); //一旦等待,就会释放锁,等待后,被唤醒,就从这里醒来                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                 }                  //有了资源,你消费                 System.out.println(student.name + "===" + student.age);                 //修改标记                 student.flag = false;                 student.notify();             }         }      } }   
文章来源: https://blog.csdn.net/qq_39077696/article/details/92657120
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!