Java中线程通信及线程虚假唤醒的示例分析

发布时间:2021-06-28 11:47:05 作者:小新
来源:亿速云 阅读:120

这篇文章将为大家详细讲解有关Java中线程通信及线程虚假唤醒的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

线程通信

线程在内部运行时,线程调度具有一定的透明性,程序通常无法控制线程的轮换执行。但Java本身提供了一些机制来保证线程协调运行。

假设目前系统中有两个线程,分别代表存款和取钱。当钱存进去,立马就取出来挪入指定账户。这涉及到线程间的协作,使用到Object类提供的wait()、notify()、notifyAll()三个方法,其不属于Thread类,而属于Object,而这三个方法必须由监视器对象来调用:

三个方法解释如下:

现在用两个同步方法分别代表存钱取钱

/**
     * 存一块钱
     *
     * @throws InterruptedException
     */
    public synchronized void increase() throws InterruptedException {
        // 当余额为1,说明已经存过钱,等待取钱。存钱方法阻塞
        if (num == 1) {
            this.wait();
        }
        // 执行存钱操作
        num++;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        // 唤醒其他线程
        this.notifyAll();
    }
 
    /**
     * 取一块钱
     *
     * @throws InterruptedException
     */
    public synchronized void decrease() throws InterruptedException {
        // 当余额为0,说明已经取过钱,等待存钱。取钱方法阻塞
        if (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }

调用方法:

private int num = 0;
 
    public static void main(String[] args) {
        Test test = new Test();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存钱").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取钱").start();
    }

结果没有什么问题 

Java中线程通信及线程虚假唤醒的示例分析

线程虚假唤醒

上述线程通信看起来似乎没有什么问题,但若此时将存钱和取钱的人数各增加1,再看运行结果

private int num = 0;
 
    public static void main(String[] args) {
        Test test = new Test();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存钱1").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取钱1").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.increase();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "存钱2").start();
 
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    test.decrease();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "取钱2").start();
    }

产生的结果已经不是最初的只有0和1

Java中线程通信及线程虚假唤醒的示例分析

造成这个结果的原因就是线程间的虚假唤醒

由于目前分别有多个取款和存款线程。假设其中一个存款线程执行完毕,并使用wait释放同步监视器锁定,那其余多个取款线程将同时被唤醒,此时余额为1,如果有10个同时取钱,那余额会变为-9,造成结果错误。

因此,每次线程从wait中被唤醒,都必须再次测试是否符合唤醒条件,如果不符合那就继续等待。

由于多个线程被同时唤醒,在if(xxxx){wait();}处 if判断只会执行一次,当下一个被唤醒的线程过来时,由于if已经判断过,则直接从wait后面的语句继续执行,因此将if换成while可解决该问题,下次被唤醒的线程过来,while重新判断一下,发现上一个被唤醒的线程已经拿到锁,因此这个被虚假唤醒的线程将继续等待锁。

 /**
     * 存一块钱
     *
     * @throws InterruptedException
     */
    public synchronized void increase() throws InterruptedException {
        while (num == 1) {// 防止每次进来的唤醒线程只判断一次造成虚假唤醒,替换成while
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }
 
    /**
     * 取一块钱
     *
     * @throws InterruptedException
     */
    public synchronized void decrease() throws InterruptedException {
        while (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + ":num=" + num);
        this.notifyAll();
    }

再次运行,结果正常:

Java中线程通信及线程虚假唤醒的示例分析

关于“Java中线程通信及线程虚假唤醒的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

推荐阅读:
  1. Java多线程-线程通信
  2. Java中等待唤醒机制线程通信的原理是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java 线程

上一篇:C#中异步迭代IAsyncEnumerable应用的实现方法

下一篇:Android中怎么实现混音

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》