`

Object类wait,notify,notifyAll的使用

    博客分类:
  • java
阅读更多

这三个方法是java的基础类Object中定义的。

Java所有的类都具有线程的潜力,Java赋予的每个对象一个锁,在计算机内部工作在同一时间,只有一个对象可以持有锁,也就是说程序在同一时间只有一个程序可以运行,这里我把对象比作是一个小的程序。而多处理器,那么就另当别论了。

在这里我们首先学习一下公共方法wait,notify,notifyAll。

wait方法可以使在当前线程的对象等待,直到别的线程调用此对象的notify或notifyAll方法(注意:调用的是此对象的notify和notifyAll),并且当前运行的线程必须具有此对象的对象监视器,对象监视器我们可以从三个方法中获得,如下:

1.在执行对象实例同步方法体中,可以获得此对象的对象监视器,例子伪代码如下:

synchronized void aMethod(){

while(condition)

this.wait();

//other mothed;
}

2.通过执行对象同步synchronized正文,例子伪代码如下:

synchronized(this){

while(condition)

this.wait();

//other mothed;
}

3.对于 Class 类型的对象,可以通过执行该类的同步静态方法

这个方法可以使当前对象满足条件condition后,执行等待,当前线程对象放弃锁,cpu记录当前线程状态,以备下次回复。然后让其他线程运行,直到其他线程调用此对象的notify或notifyAll方法,此对象才会重新获得此对象的对象监视器,此对象才能重新运行。

注意:调用这个方法,必须具有对象监视器,也就是说我们必须在这三种方法选一种来获得对象监视器,如果调用此方法wait,却没用对象监视器,那么运行时会抛出IllegalMonitorStateException.

而且,在静态方法中也无法获得对象监视器,只能在Class类型的对象中,我们才可以通过调用该类的同步静态方法来获得对象监视器。

wait()
JDk文档写道

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前线程必须拥有此对象监视器 。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。

对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:

synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}

此方法只应由作为此对象监视器的所有者的线程来调用。

抛出:
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者
InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。
对于红色部分的内容,个人曾一直都不是很理解,什么叫做拥有此对象的监视器。下面我们看看代码: 

 DateFormat format = new SimpleDateFormat("yyyy-MM-dd:hh:mm:ss");
private String getTime(){
    return format.format(Calendar.getInstance().getTime());
}
private Object monitor = new Object();
    public void waitOnce(String thread, final long ms) {
    Thread waitThread = new Thread() {
        public void run() {
            synchronized (monitor) {//获得对象监视器
                try {
                    System.out.println("Thread "
                            + Thread.currentThread().getName()
                            + " Wait at " + getTime());
                    monitor.wait(ms);
                    System.out.println("Thread "
                            + Thread.currentThread().getName()
                            + " Waked at " + getTime());
                } catch (InterruptedException e) {
                }
            }
        };
    };
    waitThread.setName(thread);
    waitThread.start();
}

如果我们去掉synchronized(monitor) ,运行则会出现异常IllegalMonitorStateException。

WaitAndNotifyTest test = new WaitAndNotifyTest();
test.waitOnce("1", Long.MAX_VALUE); 

写道

Exception in thread “1″ java.lang.IllegalMonitorStateException

而加上以后就没问题了。因此个人觉得使用synchronized关键字锁定对象,也就是获得了对象的监视器了。

notify()
JDK文档 写道

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程 。选择是任意性 的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待

直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程 。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:

通过执行此对象的同步实例方法。
通过执行在此对象上进行同步的 synchronized 语句的正文。
对于 Class 类型的对象,可以通过执行该类的同步静态方法。


一次只能有一个线程拥有对象的监视器。

抛出:
IllegalMonitorStateException – 如果当前线程不是此对象监视器的所有者。

首先理解一下获得对象的监视器,简单的说就是取得了当前对象的“加锁”使用权,最简单的就是使用synchronized关键字。另外使用 synchronized修饰的方法也行。

notify方法还有一个值得提出的是它会在当前线程释放了对象锁以后随机唤醒一个在该对象上等待的线程

看看一个例子: 

public void awakeAndWait(String thread, final long ms) {
    Thread notifyThread = new Thread() {
        public void run() {
            synchronized (monitor) {
                monitor.notify();
                System.out.println("Thread "
                        + Thread.currentThread().getName() + " Notify at "
                        + getTime());
                //保持了对象锁的等待
                try {
                    Thread.sleep(ms);
                } catch (InterruptedException e) {
                }
            }
            //释放了对象锁之后的等待
            try {
                Thread.sleep(ms);
            } catch (InterruptedException e) {
            }
        };
    };
    notifyThread.setName(thread);
    notifyThread.start();
}

这个方法会唤醒一个在对象上等待的线程,并在两次sleep后退出,注意的是一个sleep是在对象锁内,而另一次则是在释放了对象锁以后,这时候运行上面2个方法得到: 

 WaitAndNotifyTest test = new WaitAndNotifyTest();
test.waitOnce("1", Long.MAX_VALUE);// 在对象上等待无限长
test.waitOnce("2", Long.MAX_VALUE);// 在对象上等待无限长
test.waitOnce("3", Long.MAX_VALUE);// 在对象上等待无限长
try {// 延迟2s
    Thread.sleep(2000);
} catch (InterruptedException e) {
}
// 在唤醒一个在对象上等待的线程,本身执行时间4s,2s是在对象锁内
//,2s是在释放了对象锁以后
test.awakeAndWait("3", 2000); 

执行结果为:

写道

Thread 1 Wait at 2011-05-06:10:57:04
Thread 2 Wait at 2011-05-06:10:57:04
Thread 3 Wait at 2011-05-06:10:57:04
Thread 3 Notify at 2011-05-06:10:57:06
Thread 1 Waked at 2011-05-06:10:57:08

2秒后唤醒了线程1,尽管它自己执行花了4s,在释放了对象锁之后的2s不会影响线程1的执行。

notifyAll()
JDK文档 写道

唤醒在此对象监视器上等待的所有线程 。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。

抛出:
IllegalMonitorStateException – 如果当前线程不是此对象监视器的所有者。

与notify稍微有一点差别的是,它会唤醒所有的等待线程。

public void awakeAll(String thread) {
    Thread notifyThread = new Thread() {
        public void run() {
            synchronized (monitor) {
                monitor.notifyAll();
                System.out.println("Thread "
                        + Thread.currentThread().getName()
                        + " Notify all at " + getTime());
            }
        };
    };
    notifyThread.setName(thread);
    notifyThread.start();
}

执行一下代码: 

 WaitAndNotifyTest test = new WaitAndNotifyTest();
test.waitOnce("1", Long.MAX_VALUE);// 在对象上等待无限长
test.waitOnce("2", Long.MAX_VALUE);// 在对象上等待无限长
test.waitOnce("3", Long.MAX_VALUE);// 在对象上等待无限长
try {// 延迟2s
    Thread.sleep(2000);
} catch (InterruptedException e) {
}
test.awakeAll("4"); 

结果为:

写道

Thread 1 Wait at 2011-05-06:10:59:15
Thread 3 Wait at 2011-05-06:10:59:15
Thread 2 Wait at 2011-05-06:10:59:15
Thread 4 Notify all at 2011-05-06:10:59:17
Thread 2 Waked at 2011-05-06:10:59:17
Thread 3 Waked at 2011-05-06:10:59:17
Thread 1 Waked at 2011-05-06:10:59:17

全部唤醒了。

总结
总结一下:大概有以下几点:

1.wait(),notify(),notifyAll()都需要在拥有对象监视器的前提下执行,否则会出现异常IllegalMonitorStateException。
2.多个线程可以同时在一个对象上等待。
3.notify()将随机唤醒一个在对象上等待的线程,没有一个都没有,则什么都不做。
4.notify()唤醒的线程,将在notify()线程释放了对象监视器以后才执行,并不是notify了以后马上执行。
5.Object的这些方法与Thread的sleep、interrupt相差还是很远的,不要混为一谈了。

 

分享到:
评论

相关推荐

    Java 同步方式 wait和notify/notifyall

    在Java中,`wait()`, `notify()`, 和 `notifyAll()` 是Java Object类的三个方法,它们在实现线程间通信和协作时扮演着关键角色。这些方法主要用于解决线程等待和唤醒的问题,是基于Java Monitor(监视器)模型的。 ...

    Java 中Object的wait() notify() notifyAll()方法使用

    Java 中Object的wait() notify() notifyAll()方法使用 在Java并发编程中,Object的wait()、notify()和notifyAll()方法是非常重要的概念,这三个方法都是Object类的方法,可以认为任意一个Object都是一种资源(或者...

    Java多线程中wait、notify、notifyAll使用详解

    Java多线程中wait、notify、notifyAll使用详解 Java中多线程编程中,wait、notify、notifyAll三个方法是非常重要的,它们都是Object对象的方法,用于线程之间的通信。下面我们将详细介绍这三个方法的使用和作用。 ...

    java之wait,notify的用法([ 详解+实例 ])

    wait方法是Object类的一个方法,用于让当前线程进入等待状态,直到其他线程调用notify或notifyAll方法来唤醒它。在wait方法中,当前线程会释放它所占有的锁,并进入等待状态。wait方法可以带有参数,指定等待的时间...

    wait_notify_demo

    `wait()`、`notify()`和`notifyAll()`是Java中的三个关键字,它们属于Object类的方法,主要用于线程间的通信,尤其在实现生产者消费者模式时发挥着重要作用。本文将深入探讨这些方法以及如何在实际场景中应用它们。 ...

    Java多线程wait和notify

    在Java中,`wait()` 和 `notify()` 方法是实现线程间通信和协作的重要工具,它们属于 `java.lang.Object` 类,这意味着所有类都默认继承了这两个方法。本文将详细探讨如何使用 `wait()` 和 `notify()` 来控制子线程...

    Java的wait(), notify()和notifyAll()使用心得

    4. **死锁和饥饿**:虽然`wait()`, `notify()`, `notifyAll()` 提供了线程间的通信,但不当使用可能导致死锁(两个或更多线程互相等待对方释放资源而无法继续执行)或饥饿(某些线程一直无法获取资源而无法执行)的...

    java中几个notify、wait使用实例

    这两个方法定义在`Object`类中,因此所有Java对象都可以作为锁来使用。在深入探讨之前,我们先了解下`wait()`、`notify()`以及`notifyAll()`的基本概念。 ### `wait()` 方法 当一个线程执行到`wait()`方法时,它会...

    Object.wait()与Object.notify()的用法详细解析

    Java中的`Object.wait()`、`Object.notify()`以及`Object.notifyAll()`是多线程编程中的核心方法,它们用于实现线程间的协作与通信。这些方法是`Object`类的最终原生(`final native`)方法,这意味着它们的实现位于...

    Java wait和notifyAll实现简单的阻塞队列

    在 Java 中,wait 和 notifyAll 是两个非常重要的方法,它们都是在 Object 类中声明的,用于实现线程之间的通信和同步。wait 方法会使调用线程进入等待状态,并释放所持有的对象锁,而 notifyAll 方法则会去唤醒当前...

    等待机制与锁机制wait notify

    `wait()`、`notify()`和`notifyAll()`是Java `Object`类的三个方法,它们用于线程间的通信。这些方法必须在同步环境中(即synchronized方法或synchronized代码块)使用,否则会抛出`IllegalMonitorStateException`...

    Java多线程同步(wait()notify()notifyAll())[文].pdf

    - 提供的Account类示例展示了如何使用synchronized关键字和wait/notify机制来模拟多线程存取同一账户的操作。 - save()和load()方法都使用了synchronized来保证并发访问的安全性。 - load()方法中使用了wait(),...

    深入理解Wait、Notify和Wait与sleep区别

    首先,`wait()`, `notify()`和`notifyAll()`是Object类中的方法,它们主要用于线程间通信和协作。这些方法只能在同步环境中(如`synchronized`块或方法)使用,否则会抛出`IllegalMonitorStateException`。它们的...

    Java的sychronized、wait和notify范例

    正确使用`wait()`、`notify()`和`notifyAll()`需要注意以下几点: - 必须在`synchronized`代码块或方法内部调用这些方法,因为它们操作的是对象的监视器锁。 - 调用`wait()`会导致当前线程暂停,所以在调用`wait()`...

    主线程去控制子线程wait与notify

    这两个方法是Java语言中的Object类提供的,因此所有的对象都可以使用。在本文中,我们将深入探讨如何使用主线程来控制子线程的`wait()`和`notify()`操作,以及它们在并发编程中的作用。 首先,`wait()`方法会导致...

    wait,notify等线程知识.pdf

    通过使用wait()、notify()和notifyAll(),或者Lock和Condition,开发者可以控制线程的执行顺序,确保共享资源的安全访问。 7. **死锁和活锁问题**: 使用wait/notify机制时,需要注意死锁和活锁问题。死锁发生在两...

    Java notify和notifyAll的区别和相同

    这两个方法都是在`Object`类中定义的,因此可以应用于任何Java对象。 首先,我们需要理解Java的同步机制。当一个线程进入`synchronized`代码块或方法时,它会获取到该对象的锁(即成为对象的监视器的所有者)。同一...

    基于Java多线程notify与notifyall的区别分析

    Java中的多线程编程是构建高效并发应用的关键技术之一,其中`wait()`、`notify()`和`notifyAll()`是Java对象锁机制中的三个关键方法,它们位于`java.lang.Object`类中,主要用于线程间通信。本文将深入探讨`notify()...

    一个理解wait()与notify()的例子

    `notify()`也是`Object`类中的方法,用于唤醒正在等待此对象锁的一个线程。当一个线程调用`notify()`时,会随机选择一个正在等待该对象锁的线程使其恢复运行。 - **`notify();`**:在`ThreadB`的`synchronized`代码...

    java面试题经典讲解2023年最新题目分析.docx

    10. HashMap什么样的类适合作为键String最为常见,因为String对象不可变,且重写了equals和hashcode方法。...sleep可在任意地方使用,wait notify notifyAll只能在synchronized块\方法中使用。sleep必须捕获异常

Global site tag (gtag.js) - Google Analytics