Java 对象 wait notify_Java中Object对象wait/notify/notifyAll方法详细解析

简书:capo 转载请注明原创出处,谢谢!

前言:

今天,我们讲讲Object中wait和notify/notifyAll这一组方法,我们来看看JDK中关于这两个方法的说明:

/**

引起当前线程等待直到另一个线程调用当前对象的notify方法或notify()方法或者一些其他的线程中断当前线程,或者一个指定的时间已经过去

* Causes the current thread to wait until another thread invokes the

* {@link java.lang.Object#notify()} method or the

* {@link java.lang.Object#notifyAll()} method for this object, or

* some other thread interrupts the current thread, or a certain

* amount of real time has elapsed.

*

* This method is similar to the {@code wait} method of one

* argument, but it allows finer control over the amount of time to

* wait for a notification before giving up. The amount of real time,

* measured in nanoseconds, is given by:

*

*

 
  

* 1000000*timeout+nanos

*

* In all other respects, this method does the same thing as the

* method {@link #wait(long)} of one argument. In particular,

* {@code wait(0, 0)} means the same thing as {@code wait(0)}.

*

* The current thread must own this object's monitor. The thread

* releases ownership of this monitor and waits until either of the

* following two conditions has occurred:

*

*

Another thread notifies threads waiting on this object's monitor

* to wake up either through a call to the {@code notify} method

* or the {@code notifyAll} method.

*

The timeout period, specified by {@code timeout}

* milliseconds plus {@code nanos} nanoseconds arguments, has

* elapsed.

*

*

* The thread then waits until it can re-obtain ownership of the

* monitor and resumes execution.

*

* As in the one argument version, interrupts and spurious wakeups are

* possible, and this method should always be used in a loop:

*

 
 

* synchronized (obj) {

* while ()

* obj.wait(timeout, nanos);

* ... // Perform action appropriate to condition

* }

*

* This method should only be called by a thread that is the owner

* of this object's monitor. See the {@code notify} method for a

* description of the ways in which a thread can become the owner of

* a monitor.

*

* @param timeout the maximum time to wait in milliseconds.

* @param nanos additional time, in nanoseconds range

* 0-999999.

* @throws IllegalArgumentException if the value of timeout is

* negative or the value of nanos is

* not in the range 0-999999.

* @throws IllegalMonitorStateException if the current thread is not

* the owner of this object's monitor.

* @throws InterruptedException if any thread interrupted the

* current thread before or while the current thread

* was waiting for a notification. The interrupted

* status of the current thread is cleared when

* this exception is thrown.

*/

public final void wait(long timeout, int nanos) throws InterruptedException {

if (timeout < 0) {

throw new IllegalArgumentException("timeout value is negative");

}

if (nanos < 0 || nanos > 999999) {

throw new IllegalArgumentException(

"nanosecond timeout value out of range");

}

if (nanos > 0) {

timeout++;

}

wait(timeout);

}

/**

* 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. The choice is arbitrary and occurs at

* the discretion of the implementation. A thread waits on an object's

* monitor by calling one of the {@code wait} methods.

*

* The awakened thread will not be able to proceed until the current

* thread relinquishes the lock on this object. The awakened thread will

* compete in the usual manner with any other threads that might be

* actively competing to synchronize on this object; for example, the

* awakened thread enjoys no reliable privilege or disadvantage in being

* the next thread to lock this object.

*

* This method should only be called by a thread that is the owner

* of this object's monitor. A thread becomes the owner of the

* object's monitor in one of three ways:

*

*

By executing a synchronized instance method of that object.

*

By executing the body of a {@code synchronized} statement

* that synchronizes on the object.

*

For objects of type {@code Class,} by executing a

* synchronized static method of that class.

*

*

* Only one thread at a time can own an object's monitor.

*

* @throws IllegalMonitorStateException if the current thread is not

* the owner of this object's monitor.

* @see java.lang.Object#notifyAll()

* @see java.lang.Object#wait()

*/

public final native void notify();

我总结了一下关于这个方法使用注意事项:

引起当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法.或者指定线程超时等待一定时间后。

这个超时时间单位是纳秒,其计算公式为: 1000000*timeout+nanos

如果使用wait(0)和wait(0,0)是等价的

如果当前对象在没有获得锁的监视器的情况下就调用wait或者notify/notifyAll方法就是抛出IllegalMonitorStateException异常

当前对象的wait方法会暂时释放掉对象监视器的锁,所以wait必须是在synchronized同步块中使用,因为synchronized同步块进入是默认是要获取对象监视器的。同理notify/notifyAll操作也要在对象获取监视器的情况下去唤醒一个等待池中的线程

wait操作还要在一个循环中使用,防止虚假唤醒

wait/notify在工作中的应用,等待通知机制(消费者-生产者模式)

一个线程修改了一个对象的值,而另一个线程感知道了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程。前者是生产者,后者是消费者。接下来我们使用wait/notify实现这个机制

package com.minglangx.object;

import java.text.SimpleDateFormat;

import java.util.Date;

/**

*

* @ClassName: WaitNotify

* @Description: 使用wait/notify实现等待通知机制

* @author minglangx

* @date 2017年9月4日 下午4:16:30

*

*/

public class WaitNotify {

public static boolean flag = true;

public static Object lock = new Object();

public static void main(String[] args){

Thread waitTHread = new Thread(new Wait(),"WaitThread");

waitTHread.start();

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

Thread notifyThread = new Thread(new Notify(),"NotifyThread");

notifyThread.start();

}

static class Wait implements Runnable{

@Override

public void run() {

//获取 lock对象监视器 并加锁

synchronized (lock) {

//当条件不满足时,继续wait,同时只是暂时释放了lock对象上的锁,并将当前对象防止到对象的等待队列中

while(flag) {

try {

System.out.println(Thread.currentThread()

+ "flag is true. wait@ "

+ new SimpleDateFormat("HH:mm:ss")

.format(new Date()));

lock.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//当条件满足时,完成工作

System.out.println(Thread.currentThread()

+ "flag is true. wait@ "

+ new SimpleDateFormat("HH:mm:ss")

.format(new Date()));

}

}

}

static class Notify implements Runnable{

@Override

public void run() {

/*

* 获取对象的监视器

*/

synchronized (lock) {

//获取对象上的锁,然后通知等待队列中的所有对象,但这个时候不会释放锁

System.out.println(Thread.currentThread()

+ " 持有锁..notify @"

+ new SimpleDateFormat("HH:mm:ss").format(new Date()));

//调用该方法后,将会把所有等待队列中的线程全部移动到同步队列中

lock.notifyAll();

//将条件置为 false

flag = false;

try {

Thread.sleep(5);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//再次加锁

synchronized (lock) {

System.out.println(Thread.currentThread()

+ " 再次持有锁..sleep @"

+ new SimpleDateFormat("HH:mm:ss").format(new Date()));

try {

Thread.sleep(5);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

}

这段代码最后输出:

4cc51d2408f3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

image.png

我们看到当调用notify并没有释放掉对象上的锁,而是要等待synchronized代码块走完在释放掉对象上的锁

这段代码向我们说明了几点

调用wait()方法后,线程状态由 running 变为等待wait, 并将当前线程放入等待队列中

notify、notifyAll方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或者notifyAll()的线程释放掉锁后,等待线程才有机会从wait()返回

notify()方法是将等待队列中一个等待线程从等待队列移动到同步队列中,而notifyAll则是将所有等待队列中的线程移动到同步队列中,被移动的线程状态由 running变为 阻塞blocked

为此我们规范一下这个等待、通知机制(消费者,生产者模式)如何编写

等待者(消费者)

编写代码步骤:

获取对象上的锁

如果条件不满足,则调用对象上的wait()方法,应该使用一个while()条件判断

条件满足则执行对应的业务逻辑

其中伪代码:

synchronized(对象) {

while(条件不满足){

对象.wait();

}

处理对应的业务逻辑

}

通知者(生产者)

编写代码步骤:

1) 获取对象上的锁

改变条件

通知所有(一个)等待在对象上的线程

对应的伪代码:

synchronized(对象) {

改变条件

对象.notifyAll();

}

总结:

使用wait或者notify()方法一定要在同步代码块中使用,而wait一般要在while循环中使用

wait/notify可以实现生产者消费者模式,其原理是调用wait时将线程放入等待队列,而调用notify时将等待队列中的线程移动到同步队列

wait/notify机制是成对出现的,它们的实现依赖于锁的同步机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值