资讯专栏INFORMATION COLUMN

Java 多线程编程之:notify 和 wait 用法

eccozhou / 3174人阅读

摘要:和简介和均为的方法暂停一个线程唤醒一个线程从以上的定义中,我们可以了解到以下事实想要使用这两个方法,我们需要先有一个对象。在中任何一个时刻,对象的控制权只能被一个线程拥有。若有多个线程处于此控制权下的状态,只有一个会被唤醒。

最近看帖子,发现一道面试题:

启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100 最后 STDOUT 中按序输出 1,2,3,4,5…100

题目要求用 Java 的 wait + notify 机制来实现,重点考察对于多线程可见性的理解。

wait 和 notify 简介

wait 和 notify 均为 Object 的方法:

Object.wait() —— 暂停一个线程

Object.notify() —— 唤醒一个线程

从以上的定义中,我们可以了解到以下事实:

想要使用这两个方法,我们需要先有一个对象 Object。

在多个线程之间,我们可以通过调用同一个对象wait()notify()来实现不同的线程间的可见。

对象控制权(monitor)

在使用 wait 和 notify 之前,我们需要先了解对象的控制权(monitor)。在 Java 中任何一个时刻,对象的控制权只能被一个线程拥有。如何理解控制权呢?请先看下面的简单代码:

public class ThreadTest {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

直接执行,我们将会得到以下异常:

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at com.xiangyu.demo.ThreadTest$1.run(ThreadTest.java:10)
    at java.lang.Thread.run(Thread.java:748)

出错的代码在:object.wait();。这里我们需要了解以下事实:

无论是执行对象的 wait、notify 还是 notifyAll 方法,必须保证当前运行的线程取得了该对象的控制权(monitor)

如果在没有控制权的线程里执行对象的以上三种方法,就会报 java.lang.IllegalMonitorStateException 异常。

JVM 基于多线程,默认情况下不能保证运行时线程的时序性

在上面的示例代码中,我们 new 了一个 Thread,但是对象 object 的控制权仍在主线程里。所以会报 java.lang.IllegalMonitorStateException 。

我们可以通过同步锁来获得对象控制权,例如:synchronized 代码块。对以上的示例代码做改造:

public class ThreadTest {
    public static void main(String[] args) {
        Object object = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object){ // 修改处
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

再次执行,代码不再报错。

我们可以得到以下结论:

调用对象的wait()notify()方法,需要先取得对象的控制权

可以使用synchronized (object)来取得对于 object 对象的控制权

解题

了解了对象控制权之后,我们就可以正常地使用 notify 和 wait 了,下面给出我的解题方法,供参考。

public class ThreadTest {
    private final Object flag = new Object();

    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        ThreadA threadA = threadTest.new ThreadA();
        threadA.start();
        ThreadB threadB = threadTest.new ThreadB();
        threadB.start();
    }

    class ThreadA extends Thread {
        @Override
        public void run() {
            synchronized (flag) {
                for (int i = 0; i <= 100; i += 2) {
                    flag.notify();
                    System.out.println(i);
                    try {
                        flag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    class ThreadB extends Thread {
        @Override
        public void run() {
            synchronized (flag) {
                for (int i = 1; i < 100; i += 2) {
                    flag.notify();
                    System.out.println(i);
                    try {
                        flag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
发散:notify()notifyAll()

这两个方法均为 native 方法,在JDK 1.8 中的关于notify()的JavaDoc如下:

Wakes up a single thread that is waiting on this object"s monitor. If any threads are waiting on this object, one of them is chosen to be awakened.

译为:

唤醒此 object 控制权下的一个处于 wait 状态的线程。若有多个线程处于此 object 控制权下的 wait 状态,只有一个会被唤醒。

也就是说,如果有多个线程在 wait 状态,我们并不知道哪个线程会被唤醒。

在JDK 1.8 中的关于notifyAll()的JavaDoc如下:

Wakes up all threads that are waiting on this object"s monitor.

译为:

唤醒所有处于此 object 控制权下的 wait 状态的线程。

所以,我们需要根据实际的业务场景来考虑如何使用。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77389.html

相关文章

  • java并发编程学习线程的生命周期-wait,notify,notifyall(六)

    摘要:不释放持有的锁,释放锁。在调用方法前,必须持有锁,调用唤醒,也要持有锁。休眠一定时间后,进入就绪状态。这两个都能被方法中断当前状态。用法方获取锁判断条件,不满足继续满足执行其他业务方获取锁改变条件通知为什么是而不是会一直循环,直到条件满足。 sleep和wait sleep是Thread类的方法,wait是Object的方法。 sleep可以到处使用,wait必须是在同步方法或者代码...

    Terry_Tai 评论0 收藏0
  • Java并发编程线程间通讯(上)wait/notify机制

    摘要:用线程表示维修的过程维修结束把厕所置为可用状态维修工把厕所修好了,准备释放锁了这个维修计划的内容就是当维修工进入厕所之后,先把门锁上,然后开始维修,维修结束之后把的字段设置为,以表示厕所可用。 线程间通信 如果一个线程从头到尾执行完也不和别的线程打交道的话,那就不会有各种安全性问题了。但是协作越来越成为社会发展的大势,一个大任务拆成若干个小任务之后,各个小任务之间可能也需要相互协作最终...

    Lionad-Morotar 评论0 收藏0
  • Java并发编程线程间通讯(下)-生产者与消费者

    摘要:前文回顾上一篇文章重点唠叨了中协调线程间通信的机制,它有力的保证了线程间通信的安全性以及便利性。所以同一时刻厨师线程和服务员线程不会同时在等待队列中。对于在操作系统中线程的阻塞状态,语言中用和这三个状态分别表示。 前文回顾 上一篇文章重点唠叨了java中协调线程间通信的wait/notify机制,它有力的保证了线程间通信的安全性以及便利性。本篇将介绍wait/notify机制的一个应用...

    lufficc 评论0 收藏0
  • Java 线程核心技术梳理(附源码)

    摘要:本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,的使用,定时器,单例模式,以及线程状态与线程组。源码采用构建,多线程这部分源码位于模块中。通知可能等待该对象的对象锁的其他线程。 本文对多线程基础知识进行梳理,主要包括多线程的基本使用,对象及变量的并发访问,线程间通信,lock的使用,定时器,单例模式,以及线程状态与线程组。 写在前面 花了一周时...

    Winer 评论0 收藏0
  • Java基础线程Thread

    摘要:在程序开发中一定遇到并发编程的场景虽然我们大部分时间并不直接使用但是是多线程的基础面试中也会总是被问到与线程有关的问题那么线程都有哪些知识呢最近在研究线程的源码的时候也总结了关于线程一些基本知识线程是什么线程是轻量级的进程是操作系统调度任务 在程序开发中, 一定遇到并发编程的场景, 虽然我们大部分时间并不直接使用Thread, 但是Thread是多线程的基础, 面试中也会总是被问到与线...

    tomlingtm 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<