多线程(四):wait 和 notify
介绍了多线程编程中要用到的 wait 和 notify 方法,并分析了 wait 方法和 sleep 方法的异同
·
多线程(四):wait 和 notify
引入 wait 和 notify 是为了能够从应用层面,干预多个不同线程的执行顺序。这里说的干预,不是影响系统的线程调度策略(仍是随机调度),而是相当于在应用程序代码中,让后执行的线程,主动放弃被调度的机会,就可以让先执行的线程先执行完对应代码了。
1. wait() 方法
wait 内部做的三件事:
- 释放锁
- 进入阻塞等待
- 当其他线程调用 notify 的时候,wait 解除阻塞,并重新尝试获取这个锁
【注意】wait 方法必须在 synchronized 里面使用
wait 结束等待的条件:
- 其他线程调用该对象的 notify 方法
- wait 等待时间超时(wait 方法提供一个带有 timeout 参数的版本,用来指定等待时间)
- 其他线程调用该等待线程的 interrupt 方法,导致 wait 抛出 InterruptedExpection 异常
2. notify() 方法
notify 方法用来唤醒等待中的线程,例如:
public static void main(String[] args) {
Object locker = new Object(); //定义锁对象
Thread t1 = new Thread(() -> {
synchronized(locker) {
System.out.println("t1 wait 之前");
//抛出异常
try {
locker.wait(); //用锁对象调用 wait
}catch {
throw new RunrimeExprction(e);
}
System.out.println("t1 wait 之后");
}
});
Thread t2 = new Object(() -> {
try {
//sleep 必须写在 synchronized 外面,保证 t1 先拿到锁
Thread.sleep(5000);
synchronized(locker) {
System.out.println("t2 notify 之前");
locker.notify(); //用同一个锁对象调用 notify
System.out.println("t2 notify 之后");
}
}catch {
throw new RunrimeExprction(e);
}
});
t1.start();
t2.start();
}
【注意】notify 方法也要在 synchronized 里面使用。
wait 放到 synchronized 里面是因为要释放锁的前提是有锁;notify 其实可以不放到 synchronized 里,但是 Java 中特别约定要把 notify 放到 synchronized 中。
【注意】要确保代码先执行 wait 后执行 notify,否则 wait 无法被唤醒,代码逻辑上是有问题的
代码运行结果:
运行结果分析:
- t2 休眠 5s,保证了 t1 先拿到锁,打印 “t1 wait 之前”
- t1 执行到 wait,使 t1 释放锁,t2 结束休眠后拿到锁,打印 “t2 notify 之前”
- t2 执行到 notify,唤醒了 t1,但此时 t2 还没有释放锁,t1 处于阻塞状态,打印 “t2 notify 之后”
- t2 执行结束,释放锁,t1 打印 “t1 wait 之后”
wait 和 notify 之间是通过 Object 对象之间联系起来的,调用两个方法的对象必须一致才能实现唤醒。
多个 wait :
- 如果是不同对象调用的, 使用对应的对象调用 notify 实现唤醒
- 如果是同一对象调用的,使用 notify 会随机唤醒其中一个
3. notifyAll() 方法
notifyAll 方法用来唤醒这个对象上所有等待的线程(不常用)
【注意】这些线程 wait 返回的时候,要重新获取锁,就会产生锁竞争,导致这些线程实际上是一个一个串行执行的
4. wait 和 sleep 比较
4.1 相似点
- wait 提供了一个带有超时时间的版本,sleep 也是指定时间,都可以让线程阻塞一定时间,时间到了,继续执行
- wait 和 sleep 都能被提前唤醒,wait 通过 notify 或 interrupt 唤醒,sleep 通过 interrupt 唤醒
4.2 不同点
- wait 需要搭配 synchronized 使用,sleep 不需要
- wait 是 Object 类提供的方法,sleep 是 Thread 类提供的静态方法
- 使用 sleep 一般是在直到要阻塞多长时间的情况下,达到时间停止休眠;使用 wait 是在不知道要阻塞多久的情况下,wait 指定的超时时间是用来“兜底”的,不期望达到这个时间,一般在达到这个超时时间之前就被唤醒了
更多推荐
所有评论(0)