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

多线程的几种状态

新建          就绪         运行          阻塞         死亡

  • 新建:
    线程被创建出来
  • 就绪:
    具有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();
            }
        }

    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值