等待&通知机制

等待/通知机制

什么是等待/通知机制?

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

  1. 厨师做完一道菜的时间不确定,所以厨师将菜品放到"菜品传递台"上的时间也不确定;
  2. 服务员取到菜的时间取决于厨师,所以服务员就处于等待状态
  3. 服务员如何取到菜呢?又得取决于厨师,厨师将菜放在"菜品传递台"上,其实就相当于一种通知,这时服务员才可以拿到菜并交给就餐者。

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

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

wait() 方法

  • Object 类的方法,作用:使当前执行代码的线程进行等待,直到接到通知被中断为止。
  • 执行之后,当前线程释放锁
  • 只能在同步方法或同步块中调用 wait()方法。原因:JDK 强制的,方法调用之前必须先获得该对象的对象级别锁,如果调用时没有持有适当的锁,则抛出 IllegalMonitorStateException 异常。

notify()/notifyAll() 方法

  • Object 类的方法,用来通知那些可能等待该对象的对象锁的其他线程
  • 在执行 notify() 方法后,当前线程不能马上释放该对象锁,wait 状态的线程也不能马上获取该对象锁,需要等到执行 notify() 方法的线程执行完(退出 synchronized 代码块后)。
  • 只能在同步方法或同步块中调用。原因如上。

线程状态切换

Runnable 状态 & Running 状态

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

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

  1. 创建一个新的线程对象后,调用 start() 方法,系统为此线程分配 CPU 资源,线程进入 Runnable 状态
  2. 如果线程抢占到 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("☆☆☆☆☆");
            }
            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();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容