线程通信(1)

来源:互联网 发布:蔬菜网络采购平台 编辑:程序博客网 时间:2024/06/03 15:26

思考:一个线程修改了一个对象的值,而另一个线程感知其变化,然后进行相应的操作。整个过程开始于一个线程,执行终止于另一个线程。要如何用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接口

原创粉丝点击