线程通信(1) - wait与notify,多线程中的等待与通知机制

思考:一个线程修改了一个对象的值,而另一个线程感知其变化,然后进行相应的操作。整个过程开始于一个线程,执行终止于另一个线程。要如何用Java来实现这个功能呢?


等待/通知机制

等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作。上述两个线程通过对象O来完成交互,而对象上的wait()notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。

这里写图片描述


等待/通知的经典范式

先来看一个特殊的案例,假如俩个线程分别代表取钱者和存钱者,现在有一种特殊的要求,每当存钱者存一笔钱进去,取钱者就必须马上把钱全部取出来,不可以连续存两次,也不能连续取两次。

这就涉及到线程之间的通信,如下例子就使用了等待/通知的经典范式

public class Account{
    //封装账户编号、账户余额两个成员变量
    private String accountNo;
    private double balance;
    //标识账户中是否有存款
    private boolean flag =false;

    //取方法
    public synchronized void draw(double drawAmount){
        //flag为假就代表没钱,取方法阻塞
        if(!flag){
            wait();
        }
        else{
            //执行取钱操作
            balance -=drawAmount;
            System.out.println("账户余额为:"+balance);
            //将标识账户是否有余额的旗标设置成false
            flag = false;
            //唤醒其他线程
            notifyAll();
        }
    } 

    //存方法
    public synchronized void deposit(double depositAmount){
        //flag为真代表有钱,存方法阻塞
        if(flag){
            wait();
        }
        else{
            //执行存钱操作
            balance +=depositAmount;
            System.out.println("账户余额为:"+balance);
            //将标识账户是否有余额的旗标设置成true
            flag = true;
            //唤醒其他线程
            notifyAll();
        }
    } 
}

从以上例中可以提炼出等待/通知的经典范式,该范式分为两部分,分别针对等待方(消费者)和通知方(生产者)。
等待方遵循如下原则。

1)获取对象的锁。
2)如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
3)条件满足则执行对应的逻辑。
对应的伪代码如下。
synchronized(对象) {
    while(条件不满足) {
        对象.wait();
    }
    对应的处理逻辑
}

通知方遵循如下原则。

1)获得对象的锁。
2)改变条件。
3)通知所有等待在对象上的线程。
对应的伪代码如下。
synchronized(对象) {
    改变条件
    对象.notifyAll();
}

wait()方法与notify()方法的使用总结

1)对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法中直接调用这三个方法。

2)对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这三个方法。

3)使用wait()、notify()和notifyAll()时需要先对调用对象加锁。

4)调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列。

5)notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或notifAll()的线程释放锁之后,等待线程才有机会从wait()返回。

6)notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。

7)从wait()方法返回的前提是获得了调用对象的锁。从上述细节中可以看到,等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量做出的修改。
这里写图片描述


如果程序不使用synchronized关键字来保证同步,而是使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用wait() ,notify(),notifyAll()方法来进行线程通行了。这时候就要使用condition类来替代wait(),notify()方法了。

线程通信 - Condition接口

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值