线程间通信之等待唤醒机制

来源:互联网 发布:网络营销策划方案例文 编辑:程序博客网 时间:2024/05/15 07:05

线程间通信之等待唤醒机制

在命令式编程中,线程之间的通信机制有2种:共享内存和消息传递

在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。在消息传递的并发模型里,线程之间内有公共状态,线程之间必须通过发送消息来进行显示通信。

等待唤醒机制

等待唤醒机制,是指一个线程A调用了对象Object的wait()方法进入等待状态,而另一个线程B调用了对象Object的notify()或者notifyAll()方法,线程A收到了通知后,从对象Object的wait()方法返回,进而执行后续操作。

上述两个线程通过Object对象来完成交互,而对象上的wait()notify()/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方的交互工作。

等待/通知的相关方法是任意Java类对象都具备的,Object类都有实现

等待/通知的相关方法

  • notify(): 通知一个对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获得了对象的锁。

  • notifyAll():通知所有等待在该对象上的线程

  • wait():调用该方法的线程进入等待状态,只有等待另外的线程的通知或被中断才会返回,需要注意,调用wait()方法后,会释放对象锁。

  • wait(long mills):超时等待一段时间(单位毫秒),如果没有就超时返回。

  • wait(long mills , int n):对超时时间更细粒度的控制,可以达到纳秒

调用wait()notify()/notifyAll()需要注意的细节

  • 使用wait()notify()/notifyAll()时需要先对调用对象加锁

  • 调用wait()方法后,线程状态由运行状态(Running)变为等待状态(Waiting),并将当前线程放置到对象的等待队列。

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

  • notify()方法将等待队列中的一个等待线程移动到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程有等待状态(Waiting)变为阻塞状态(Blocked)

  • wait()方法返回的前提是获得了调用对象的锁。

从上述细节中可以看到,等待唤醒机制依托于同步机制(同步对象锁),其目的就是确保wait()方法返回时能够感应到通知线程对变量做出修改。


在Thread的API与wait()有一个很像的方法sleep()方法,他们2者的区别如下:

sleep()在休眠的过程中是一直持有锁的,而wait()是执行之后释放锁的


等待/通知机制示例

public class Test {    private final static Object lock = new Object();    private static boolean flag = true;    public static void main(String[] args) {        ThreadA threadA = new ThreadA();        threadA.setName("threadA");        ThreadB threadB = new ThreadB();        threadB.setName("threadB");        threadA.start();        threadB.start();    }    public static class ThreadA extends Thread {        @Override        public void run() {            synchronized (lock) {                while (flag) {                    System.out.println(getName() + ":开始执行");                    try {                        System.out.println(getName() + ":进入等待状态");                        lock.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                System.out.println(getName() + ":执行结束");            }        }    }    public static class ThreadB extends Thread {        @Override        public void run() {            synchronized (lock) {                System.out.println(getName() + ":开始执行");                lock.notify();                System.out.println(getName() + ":通知释放锁");                flag = false;                System.out.println(getName()+":执行结束");            }        }    }}

打印结果:

threadA:开始执行threadA:进入等待状态threadB:开始执行threadB:通知释放锁threadB:执行结束threadA:执行结束

在这个示例中,ThreadA和ThreadB的状态描述:

当ThreadA线程获取了lock对象锁,然后调用lock.wait()方法。从而放弃了lock锁,ThraedA线程进入等待队列中(等待池),ThreadA进入等待状态。由于ThreadA释放了锁,此时ThreadB获得了lock锁,并调用notify()方法,ThreadA从等待队列(等待池)进入同步队列(锁池),此时ThreadA由等待状态(Waiting)变为阻塞状态(Blocked)。ThreadB释放了lock锁后,ThreadA再次获取了lock锁,wait()方法返回继续执行

image


原创粉丝点击