java多线程循环打印案例以及虚假唤醒可能出现的程序卡住问题

1.循环打印

public class OutWord {

    private String word = "输出A";

    public synchronized void outA() throws InterruptedException {
        if (word.equals("B输出")){
            this.wait();
        }

        //输出word
        System.out.println("word = " + word);
        word="B输出";
        this.notify();
    }

    public synchronized void outB() throws InterruptedException {
        if (word.equals("输出A")){
            this.wait();
        }

        //输出word
        System.out.println("word = " + word);
        word="输出A";
        this.notify();
    }

}
public class Test {
    public static void main(String[] args) {
        OutWord outWord = new OutWord();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    outWord.outA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程一").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    outWord.outB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程二").start();
    }
}

输出结果

word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
.........

2.虚假唤醒

2.1代码演示

加入我们增加两个线程 新增的线程分别是 打印A 和 打印B

public class Test {
    public static void main(String[] args) {
        OutWord outWord = new OutWord();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    outWord.outA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程一").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    outWord.outB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程二").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    outWord.outA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程三").start();

        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    outWord.outB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程四").start();
    }
}
public class OutWord {

    private String word = "输出A";

    public synchronized void outA() throws InterruptedException {
        if (word.equals("B输出")){
            this.wait();
        }

        //输出word
        System.out.println("word = " + word);
        word="B输出";
        this.notify();
    }

    public synchronized void outB() throws InterruptedException {
        if (word.equals("输出A")){
            this.wait();
        }

        //输出word
        System.out.println("word = " + word);
        word="输出A";
        this.notify();
    }

}

输出结果

word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = B输出
word = B输出
word = B输出
word = B输出

2.2 什么是虚假唤醒

可以看到打印顺序已经乱了,出现了虚假唤醒 : 现在有四条线程执行打印任务 假设其中两条打印B的线程先后抢到锁,但是都不满足if条件,进入wait()等待,并释放了锁,此时打印A的线程拿到了锁,并且打印了A,随机唤醒了打印B的一条线程,线程打印B后又唤醒了另外一条打印B的线程,此时就会出现打印乱序的情况了 原因:notify随机唤醒一条线程,被唤醒的线程将会继续执行wait后的代码

2.2.3 解决办法

将 if条件换成 while 线程被唤醒后依然要进行条件判断 如果不满足,继续wait 释放锁,等待被唤醒

public class OutWord {

    private String word = "输出A";

    public synchronized void outA() throws InterruptedException {
      //  System.out.println(Thread.currentThread().getName()+"拿到了锁");
        while (word.equals("B输出")){
          //  System.out.println(Thread.currentThread().getName()+"等待被唤醒");
            this.wait();
       //     System.out.println(Thread.currentThread().getName()+"被唤醒,继续运行");
        }
        //输出word
        System.out.println("word = " + word);
        word="B输出";
        //System.out.println(Thread.currentThread().getName()+"随机唤醒一条线程");
        this.notify();
       // System.out.println(Thread.currentThread().getName()+"执行完毕");
    }

    public synchronized void outB() throws InterruptedException {
       // System.out.println(Thread.currentThread().getName()+"拿到了锁");
        while (word.equals("输出A")){
        //    System.out.println(Thread.currentThread().getName()+"等待被唤醒");
            this.wait();
         //   System.out.println(Thread.currentThread().getName()+"被唤醒,继续运行");
        }

        //输出word
        System.out.println("word = " + word);
        word="输出A";
       // System.out.println(Thread.currentThread().getName()+"随机唤醒一条线程");
        this.notify();
       // System.out.println(Thread.currentThread().getName()+"执行完毕");
    }

}

输出结果

word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出
word = 输出A
word = B输出

打印顺序已经恢复正常,那是不是就没有其它问题了呢? 我们多运行几次 发现输出台居然卡住了 是死锁了吗

image-20220226130432248

3.解决虚假唤醒问题后出现的程序卡死问题

首先我们分析 锁的都是同一个对象 wait会释放锁,被唤醒后也需要重新竞争锁对象

死锁的四个条件 除了互斥 其他三个都不满足 所以不可能死锁

而代码中,只使用了 while循环 是while的原因吗?

 while(条件){
        this.wait()
}

下一贴详细说明问题出现的原因 欢迎留言说下你的答案

#实习信息##学习路径##Java#
全部评论
当年学了一段时间后就放弃了
点赞 回复 分享
发布于 2022-02-28 17:11

相关推荐

给🐭🐭个面试机会吧:我boss直聘天天有家教跟我打招呼😓
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
昨天 11:44
你在各大软件投了一份又一份,你打招呼的hr一个接一个,但是只要你投过的,很快就下线了,没关系你的能量是很强,你看过的岗位招到人的速度都增加了。朋友们一个个拿着丰厚的实习回报,你却默默在家刷新邮箱,等待着那寥寥无几的面试通知。你每天一睁眼就狂投简历,你一有面试邀约就点确认。过年亲戚们围坐聊天,谈论着他们孩子的职场成就,你试图插话说自己面试过的公司数量,但他们显然不太感兴趣。你在心里自嘲,觉得他们不懂面试的艰辛、不懂得每一次面试机会的珍贵,不懂得一张张精心准备的简历背后的努力。笑你那个小侄子只会在网上刷刷职位,而你已经是各大招聘网站的常客。亲戚们夸赞自己孩子一年的成就,儿子的新工作,女儿的晋升,而...
龚新化:这帖删了呗,这跟我朋友有点相似,不过我是无所谓的😀,没什么感觉,我不轻易破防的,但是我一个朋友可能有点汗流浃背了😕,他不太舒服想睡了,当然不是我哈,我一直都是行的,以一个旁观者的心态看吧,也不至于破防吧😃,就是想照顾下我朋友的感受,他有点破防了,还是建议删了吧😯,当然删不删随你,因为我是没感觉的,就是为朋友感到不平罢了🥺
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客企业服务