什么是线程(下篇)
wait 和 notify
由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.
完成这个协调工作, 主要涉及到三个方法
wait() / wait(long timeout):
让当前线程进入等待状态.
notify() / notifyAll()
: 唤醒在当前对象中等待的线程.
注意: wait, notify, notifyAll
都是 Object
类的方法.
wait()方法
wait做了三件事:
1.让当前正在执行代码的线程进行阻塞等待. (让这个线程的PCB从就绪队列切换到等待对列中)并随时准备接受通知.
2.释放当前锁、(要想使用wait/notify
,必须搭配synchronized
. 需要先获取到锁,才有资格谈wait
.)释放当前锁是为了后续的线程更好的去竞争这把锁的资源
3.满足一定的条件被唤醒时, 重新尝试获取到这个锁.
注意: 其中1和2是要原子的完成,而且wait
要搭配 synchronized
来使用. 脱离 synchronized
使用 wait
会直接抛出异常.
wait 结束等待的条件:
1)其他线程调用该对象的 notify
方法.
解释:wait
和notify
都是Object
的方法.比如线程1中的对象1调用了wait.
必须要有个线程2 .也调用对象线程1的notify,
才能唤醒线程1.如果是线程2,调用了对象2的notifiy
,就无法唤醒线程1 .
2)wait
等待时间超时 (wait
方法提供一个带有 timeout
参数的版本, 来指定等待时间).
3)其他线程调用该等待线程的 interrupted
方法, 导致 wait
抛出 InterruptedException
异常.
public class ThreadDemo18 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object){
System.out.println("wait之前!");
object.wait();
System.out.println("wait之后!");
}
}
}
//运行结果:
//wait之前!
notify()方法
notify
方法只是唤醒某一个等待线程. 使用notifyAll
方法可以一次唤醒所有的等待线程.
1)也要放到synchronized
中使用 该方法是用来通知那些可能等待该对象的对象锁的
其它线程,对其发出通知notify
,并使它们重新获取该对象的对象锁
2)notify
操作是一次唤醒一个线程.如果有多个线程都在等待中,调用notify相当于随机唤醒了一个.其他线程仍保持原状.(并没有 "先来后到")
3)调用notify
这是通知对方被唤醒,但是调用notify
本身的线程并不是立即释放锁,而是要等待当前的synchronized
代码块执行完才能释放锁.(notify
本身不会释放锁)
/**
* notify
*/
public class ThreadDemo19 {
static class WaitTask implements Runnable{
private Object locker = null;
public WaitTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
//进行wait()线程
synchronized (locker){
System.out.println("wait开始!");
try {
locker.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("wait 结束!");
}
}
}
static class NotifyTask implements Runnable{
private Object locker = null;
public NotifyTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
//进行notify()线程
synchronized (locker){
System.out.println("notify开始!");
//一次只能唤醒一个
//locker.notify();
//全部唤醒
locker.notifyAll();
System.out.println("notify结束!");
}
}
}
public static void main(String[] args) throws InterruptedException {
//因为WaitTask()和NotifyTask()是两个类,不好调用
//可以新建一个对象,去专门负责调用
Object locker = new Object();
Thread t10 = new Thread(new WaitTask(locker));
Thread t11 = new Thread(new WaitTask(locker));
Thread t2 = new Thread(new NotifyTask(locker));
t10.start();
t11.start();
Thread.sleep(3000);
t2.start();
}
}
wait 和 sleep 的对比(面试题)
其实理论上 wait
和 sleep
完全是没有可比性的,因为一个是用于线程之间的通信的,一个只是让线程阻塞一段时间,(其实没有明确的关联关系.)
唯一的相同点就是都可以让线程放弃执行一段时间.当然为了面试的目的,我们还是总结下:
wait
需要搭配synchronized
使用.sleep
不需要.wait
是Object
的方法sleep
是Thread
的静态方法.sleep
操作是指定一个固定时间来阻塞等待.wait
的话既可以指定时间,也可以无限等待.wait
唤醒可以通过notify
或者interrupt
或者时间到来唤醒,sleep
唤醒通过时间到或者interrupt
唤醒.的静态方法.wait
主要的用途就是为了协调线程之间的先后顺序这样的场景并不适合使用sleep
.sleep
单纯让该线程休眠并不涉及到多个线程的配合.