【JavaEE】多线程 -- 线程等待wait和notify
目录
- 线程等待的目的
- wait方法
- wait的使用
- notify方法
- notifyall方法
- wait和sleep的区别(重点)
线程等待的目的
- 在多线程的程序中, 我们线程通常需要等待某个线程来配合执行完成任务. 比如生活中我们打篮球, 要先传球给投篮的那个队友, 队友才能有机会把篮球投进去. 在传给他求之前, 负责投篮的那个队友一直在等待其他队友给他传球. 线程之间也是这样
- 这里我们就可以用这4个方法来决定线程执行顺序的目的
- 以上方法都属于 Object 类的方法,只要是类对象(不包含基本数据类型)都可以使用。
wait方法
- 有人说线程等待不是前面说过了吗, join就可以了.
- join 也算是一种控制线程顺序的方式, 但是本质上的区别在于,join 是一个线程等待另一个线程执行完毕后再继续执行,wait() 就是让线程停下来等一等,另一个线程调用notify(并且执行完锁里面的代码) 就可以把该线程唤醒就能够继续执行,多线程之间针对某一个代码块的执行顺序就很灵活
- wait方法做的事情
1.释放当前的锁
2.等待其他线程的通知(当前线程进入阻塞状态)
3.当通知到达后, 从阻塞状态回到就绪状态, 并且重新获取到锁.
- 这里我们要保证第一步和第二步是原子的, 也就是要合并在一起执行.如果不是原子的, 那么问题就大了. t1线程错过会t2线程的通知, 那么就无人唤醒t1线程了.
我们在使用 wait() 方法的大前提是必须在 synchaonized(加锁) 的状态下(因为我们wait方法要做的第一件事情解释解锁),如果在没有加锁的代码块中使用 wait() 方法就会抛出 “IllegalMonitorStateException”异常。
- 注意wait方法解锁后, 并不会对后面的代码造成线程不安全的问题,
- wait() 结束等待的条件:
- 其他线程调⽤该对象的 notify ⽅法.
- wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间).
- 其他线程调用该等待线程的 interrupted() 方法(判断线程是否设置了中断标志位,这是Thread 中断线程的一个机制),导致抛出InterruptedException 异常
wait的使用
public class demo19 {public static void main(String[] args) {Object locker = new Object();Thread t1 = new Thread(()->{synchronized (locker){System.out.println("t1 wait之前");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 wati之后");}});t1.start();}
}
- 可以看到我们的t1线程就一直阻塞在这里, 等待其他线程把他唤醒.
- 当然我们在实际场景中, 我们也有不能一直等待的情况. 所以我们的wati还有两个版本
notify方法
- notify() 方法的作用就是用来唤醒等待的线程的。
- 该方法是在(锁住的代码同步)同步方法和同步块中使用, 用于通知那些可能等待该对象锁的其他线程, 唤醒这个线程, 并且释放对象锁让这个线程拿到.
- 如果有多个线程等待,则有线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 “先来后到”)
- 在使用完notify方法之后, 这个线程并不会立即释放对象锁, 直到这个线程执行完(到达}), 才会释放这个对象锁. 这也意味着, 我们等待对象锁的线程被唤醒后, 并不能马上拿到对象锁. 必须等占有对象锁的线程把代码执行完, 才能拿到这把锁.
notifyall方法
- notify⽅法只是唤醒某⼀个等待线程. 使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程.
notify 只唤醒等待队列中的⼀个线程. 其他线程还是乖乖等着
notifyAll ⼀下全都唤醒, 需要这些线程重新竞争锁
wait和sleep的区别(重点)
- wait设定超时时间是后手, 主要是通信中, 等待其他线程唤醒.