您的位置:首页 > 其它

等待&通知机制

2018-02-06 15:03 239 查看

等待/通知机制

等待通知机制
什么是等待通知机制

如何实现等待通知机制
wait 方法

notifynotifyAll 方法

线程状态切换
Runnable 状态 Running 状态

Running 状态 - Blocked 状态

Blocked 状态 - Runnable 状态

Dead 状态

waitnotify模式的注意事项

经典案例生产者消费者模式实现

实战等待通知之交叉备份

什么是等待/通知机制?

举例说明,厨师和服务员之间的交互

1. 厨师做完一道菜的时间不确定,所以厨师将菜品放到”菜品传递台”上的时间也不确定;

2. 服务员取到菜的时间取决于厨师,所以服务员就处于等待状态

3. 服务员如何取到菜呢?又得取决于厨师,厨师将菜放在”菜品传递台”上,其实就相当于一种通知,这时服务员才可以拿到菜并交给就餐者。

如何实现等待/通知机制?

一句话总结:wait 使线程停止运行,而 notify 使停止的线程继续运行。

wait() 方法

Object 类的方法,作用:使当前执行代码的线程进行等待,直到接到通知被中断为止。

执行之后,当前线程释放锁

只能在同步方法或同步块中调用 wait()方法。原因:JDK 强制的,方法调用之前必须先获得该对象的对象级别锁,如果调用时没有持有适当的锁,则抛出 IllegalMonitorStateException 异常。

notify()/notifyAll() 方法

Object 类的方法,用来通知那些可能等待该对象的对象锁的其他线程

在执行 notify() 方法后,当前线程不能马上释放该对象锁,wait 状态的线程也不能马上获取该对象锁,需要等到执行 notify() 方法的线程执行完(退出 synchronized 代码块后)。

只能在同步方法或同步块中调用。原因如上。

线程状态切换

Runnable 状态 & Running 状态

Runnable:就绪状态,随时可能被 CPU 调度执行

Running:运行状态,线程获取 CPU 权限进行执行

创建一个新的线程对象后,调用 start() 方法,系统为此线程分配 CPU 资源,线程进入 Runnable 状态

如果线程抢占到 CPU 资源,此线程进入 Running 状态

Running 状态 -> Blocked 状态

blocked:阻塞状态,线程放弃了 CPU 使用权,暂时停止运行,直到线程进入就绪状态。分为三种:

1. 等待阻塞:调用 wait()方法,让线程等到某工作的完成

2. 同步阻塞:synchronized 获取对象锁失败(可能锁被占用)

3. 其他阻塞:调用 sleep() | join() | 发出 IO 请求时,线程进入阻塞。

线程调用 sleep() 方法,主动放弃占用的处理器资源

线程调用了阻塞式 IO 方法,在该方法返回前,该线程被阻塞

线程试图获得一个同步监视器,但该监视器正被其他线程所持有

线程调用 wait() 方法,等待某个通知

程序调用了 suspend 方法将该线程挂起。(此方法容易死锁,避免使用)

Blocked 状态 -> Runnable 状态

调用 sleep() 方法后,sleep()超时

线程调用的阻塞 IO 已经返回,阻塞方法执行完毕

线程成功获得了试图同步的监视器

线程正在等到通知,其他线程发出了通知(notify() | notifyAll)

处于挂起状态的线程调用了 resume() 恢复方法

Dead 状态

线程执行完了或者异常退出了 run() 方法,结束生命周期。

每个锁对象都有两个队列:

- 就绪队列:存储将要获得锁的线程,一个线程被唤醒后,进入就绪队列,等到 CPU 调度

- 阻塞队列:存储被阻塞的线程,一个线程被 wait() 后,进入阻塞队列,等待下一次被唤醒

wait/notify模式的注意事项

wait 释放锁,notify 不释放锁

当线程处于 wait 状态时,使用 interrupt() 方法会出现 InterruptedException 异常

wait(long) 方法的作用:等待某一个时间内是否有线程对锁进行唤醒,如果超过时间则自动唤醒

如果通知过早,则会打乱程序正常的运行逻辑。(wait 状态的线程不会被通知

wait 等待的条件发生变化,也容易造成程序逻辑的混乱

经典案例:生产者/消费者模式实现

实战:等待/通知之交叉备份

创建 20 个线程,其中 10 个线程是将数据备份到 A 数据中,另外 10 个线程是将数据备份到 B 数据库中,并且备份 A 数据库和 B 数据库是交叉进行的。

public class DBTools {

// 确保备份 "★★★★★" 首先执行,然后与 "☆☆☆☆☆" 交替进行备份
volatile private boolean prevIsA = false;

synchronized public void backupA() {
try {
while (prevIsA) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("★★★★★");
}
prevIsA = true;
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

synchronized public void backupB() {
try {
while (!prevIsA) {
wait();
}
for (int i = 0; i < 5; i++) {
System.out.println("☆☆☆☆☆");
}

c539
prevIsA = false;
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


public class BackupA extends Thread {
private DBTools dbTools;

public BackupA(DBTools dbTools) {
super();
this.dbTools = dbTools;
}

@Override
public void run() {
dbTools.backupA();
}
}


public class BackupB extends Thread {
private DBTools dbTools;

public BackupB(DBTools dbTools) {
super();
this.dbTools = dbTools;
}

@Override
public void run() {
dbTools.backupB();
}
}


public class Run {

public static void main(String[] args) {

DBTools dbTools = new DBTools();

for (int i = 0; i < 20; i++) {
BackupB output = new BackupB(dbTools);
output.start();
BackupA input = new BackupA(dbTools);
input.start();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: