概述
java中多线程常用到wait和notify等方法,但是对此缺乏一个清晰的认识,本文主要介绍JAVA中wait和notify以及notifyAll的使用.
Wait
/**
* 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.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until 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 thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
* 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.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
* @throws InterruptedException if any thread interrupted the
* current thread before or while the current thread
* was waiting for a notification. The <i>interrupted
* status</i> of the current thread is cleared when
* this exception is thrown.
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
wait方法调用后会使持有该对象的线程把对象的控制权交出去,也就是释放锁,然后原来的线程处于等待状态.
wait和notify是用来做线程通信的,wait调用后必须是另一个线程中调用notify或者notifyAll来唤醒线程.
并且wait方法只能在拥有前对象的镜像中调用,也就是在synchronized同步代码块中调用,否则会报IllegalMonitorStateException异常.
Notify
/**
* 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.
* <p>
* 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.
* <p>
* 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:
* <ul>
* <li>By executing a synchronized instance method of that object.
* <li>By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
* <li>For objects of type {@code Class,} by executing a
* synchronized static method of that class.
* </ul>
* <p>
* 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()
*/
通知某个正在等待这个对象的控制权的线程可以继续运行,注意这里唤醒的是一个单一线程使该线程重新获取这个对象的锁,获取锁并不代表就可以马上执行.
新唤醒的线程不会马上执行,而是和其他线程竞争执行权限,除非该线程设置了特有的线程优先级.
同样的必须在synchronized同步代码块中调用,否则会报IllegalMonitorStateException异常.
NotifyAll
/**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* {@code wait} methods.
* <p>
* The awakened threads will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened threads
* will compete in the usual manner with any other threads that might
* be actively competing to synchronize on this object; for example,
* the awakened threads enjoy no reliable privilege or disadvantage in
* being the next thread to lock this object.
* <p>
* 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.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notify()
* @see java.lang.Object#wait()
*/
唤醒等待获取此对象控制权限的所有线程,同样的这些线程不会马上执行,而是去CPU哪里竞争执行权限.
也必须在synchronized同步代码块中执行,,否则会报IllegalMonitorStateException异常.
使用-例子-1
package com.sefon.gateway;
/**
* @Description:
* @author: zhouqiang
* @version: 1.0, Apr 4, 2019
*/
public class OutputThread implements Runnable {
private int num;
private Object lock;
public OutputThread(int num, Object lock) {
super();
this.num = num;
this.lock = lock;
}
public void run() {
try {
while(true){
synchronized(lock){
lock.notifyAll();//如果此处先wait,在notify不会打印任何结果
lock.wait();
Thread.sleep(1000);
System.out.println(num);
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args){
final Object lock = new Object();
Thread thread1 = new Thread(new OutputThread(1, lock));
Thread thread2 = new Thread(new OutputThread(2, lock));
thread1.start();
thread2.start();
}
}
这是一个轮询打印数字的多线程演示,为何lock先wait再notifyAll不会打印任何东西,而必须先notify才行呢?
分析:如果先wait,那线程1 2先后启动,然后都wait了那这样线程1 2都会等待另一个线程来notify。
但是显然这个线程3是不存在的,这样就没人来唤醒这两个等待中的线程,所以wait之后的代码便不会再执行了.
使用-例子-2
再来看下面一个简单的例子
WaitThread只负责wait然后执行业务代码
package com.sefon.gateway;
/**
* @Description:
* @author: jackromer
* @version: 1.0, Apr 4, 2019
*/
public class WaitThread implements Runnable {
private int num;
private Object lock;
public WaitThread(int num, Object lock) {
super();
this.num = num;
this.lock = lock;
}
public void run() {
try {
synchronized(lock){
System.out.println(Thread.currentThread().getName() + " :wait for notify");
lock.wait();//wait调用后, wait之后的代码就暂时不会再执行了, 必须等到其他线程notifyAll, 直到当前线程重新在CPU哪里竞争到了object的锁,当前线程的后序代码才会执行 .
System.out.println(Thread.currentThread().getName() + " :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :" + num);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
Thread waitThread1 = new Thread(new WaitThread(1, lock));
Thread waitThread2 = new Thread(new WaitThread(2, lock));
Thread waitThread3 = new Thread(new WaitThread(3, lock));
Thread notifyThread = new Thread(new NotifyThread(lock));//notifyThread 用来唤醒之前wait的所有线程
waitThread1.start();
waitThread2.start();
waitThread3.start();
Thread.sleep(2000);//此处sleep是为了让wait的三个线程先启动,后面再启动notify线程。
notifyThread.start();
}
}
NotifyThread只负责唤醒wait object的所有线程
package com.sefon.gateway;
/**
* @Description:
* @author: jackromer
* @version: 1.0, Apr 4, 2019
*/
public class NotifyThread implements Runnable {
private Object lock;
public NotifyThread(Object lock) {
super();
this.lock = lock;
}
public void run() {//用于notify 所有等待此对象的线程
synchronized(lock){
lock.notifyAll();//注意,此处notifyAll虽然唤醒了所有等待object的线程,但是不一定按之前线程的先后顺序继续执行,而是谁先去CPU哪里竞争到锁谁就先执行
System.out.println("I am notify thread ,i will wake up all threads which are waiting for this object.");
}
}
}
启动main方法运行后的结果:
//分析:线程执行顺序分别为0 2 1,这三个线程都会等待notify
Thread-0 :wait for notify
Thread-2 :wait for notify
Thread-1 :wait for notify
//notifyAll线程唤醒了线程1 2 3,1 2 3线程重新去CPU哪里获取执行权限,明显的他们抢到执行权限的顺序为2 3 1
I am notify thread ,i will wake up all threads which are waiting for this object.
Thread-1 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :2
Thread-2 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :3
Thread-0 :i have bean waked up and i get the lock from cpu, i am free ,i will continue to process..., my num is :1
线程的生命周期
总结
wait notify和notifyAll如果光看注释,可能不那么容易理解,最关键的还是试验一下,会有不一样的收获,但只要记住一点这三个是用来做线程通信的,必须在同步代码块中执行,他们都是object顶层对象的方法,所以你应该清楚这三个和线程没关系而是和对象的锁有关系,而线程只是用到了锁而已关系结构可以理解为这样:
OBJECT(只有一个) — LOCK(只有一个) — THREADS(线程可以有很多个,但都必须用同一个对象的同一个锁)。
一盏灯, 一片昏黄; 一简书, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞。