• 线程等待与唤醒


    目录

    概念

    等待方法 

    唤醒方法 

    唤醒和阻塞具体的使用 

    wait和sleep的区别


    概念

    • 在我们多线程开发的时候,有时候会有让一些线程先执行的,这些线程结束后,其他线程再继续执行,列如我们生活中打球,球场的每个人都是一个线程,那一个球员必须先传球给另一个球员,那么另一个球员才能投篮,这就是一个线程的一个动作执行完,另一个线程的动作才能执行
    • 线程间的等待与唤醒机制,wait和notify是Object的方法,用于线程的等待与唤醒,必须搭配着synchronized来使用,脱离 synchronized 使用 wait 会直接抛出异常

    等待方法 

     

    • 痴汉方法,死等,线程进入阻塞态(WAITING),直到有其他线程调用notify方法唤醒

     

    • 等待一段时间,若再该时间内线程被唤醒,则继续执行,若超过了相应时间还没有其他线程唤醒此线程,此线程不再等待,恢复执行

     等待方法做的事

    1. 调用wait方法的前提是获得这个对象的锁(synchronized对象锁,如果有多个线程取竞争这个锁,只有一个线程获得锁,其他线程会处于等待队列)
    2. 使当前执行代码的线程进行等待 . ( 把线程放到等待队列中 )
    3. 调用wait方法会释放锁
    4. 满足一定条件会重新尝试获得这个锁,被唤醒的之后不是立即恢复执行,而是进入阻塞队列,竞争锁

    结束等待的三个方式

    1. 其他线程调用该对象的 notify 方法.
    2. wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
    3. 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常

    唤醒方法 

    • notify()随机唤醒一个处在等待状态的线程  
    • notifyAll()唤醒所有处在等待状态的线程
    • 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的 其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
    • 如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到") 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。

    唤醒和阻塞具体的使用 

    1. package thread.wait_notify;
    2. public class waitDemo {
    3. private static class WaitTask implements Runnable{
    4. private Object lock;
    5. public WaitTask(Object lock){
    6. this.lock=lock;
    7. }
    8. @Override
    9. public void run() {
    10. synchronized (lock){
    11. System.out.println(Thread.currentThread().getName()+ "准备进入等待状态");
    12. try {
    13. lock.wait();
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }
    17. System.out.println(Thread.currentThread().getName()+ "等待结束,线程继续执行");
    18. }
    19. }
    20. }
    21. private static class Notify implements Runnable{
    22. private Object lock;
    23. public Notify(Object lock){
    24. this.lock=lock;
    25. }
    26. @Override
    27. public void run() {
    28. synchronized (lock){
    29. System.out.println("准备唤醒等待线程");
    30. //随机唤醒一个等待线程
    31. lock.notify();
    32. System.out.println("唤醒结束");
    33. }
    34. }
    35. }
    36. public static void main(String[] args) throws InterruptedException {
    37. Object lock=new Object();
    38. Thread t1=new Thread(new WaitTask(lock),"t1");
    39. Thread t2=new Thread(new WaitTask(lock),"t2");
    40. Thread t3=new Thread(new WaitTask(lock),"t3");
    41. Thread notify=new Thread(new Notify(lock),"notify");
    42. t1.start();
    43. t2.start();
    44. t3.start();
    45. Thread.sleep(100);
    46. notify.start();
    47. }
    48. }

     注意点

    必须搭配synchroized使用,不然会直接报错 

     背后工作原理解析

    •  如果是调用notifyAll,就会将这三个线程都放入阻塞队列,然后进行竞争锁资源
    • 一定要明确锁的资源是谁,引起的竞争的必须是线程调用的锁的对象一定是要一样的,如果竞争的不是同一个锁,那么就不会进入同一个阻塞队列
    • 其只有唤醒线程执行完毕,才会有阻塞队列线程的执行
    • 阻塞队列怎么理解,比如当前t1线程获得了锁资源,那么t2,t3如果想竞争这个锁,就得处于阻塞队列,当t1线程调用了wait方法,释放了锁资源,那么t2和t3就会去竞争锁资源,然后其中获得一个,依次类推,当三个线程都处于等待队列,当调用了notify线程,等待队列其中一个进入阻塞队列,但是阻塞队列就算只有一个线程,也不会立即得到锁,因为notify线程也会占用锁,必须等notify线程结束,释放锁

    wait和sleep的区别

    • 其实理论上 wait sleep 完全是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻塞一段时间, 唯一的相同点就是都可以让线程放弃执行一段时间
    • 如果有共性就先介绍共性,如果没有,分别介绍即可
    • wait方法是Object类提供的方法,需要搭配synchroized锁来使用,调用wait方法会释放锁,等待线程会被其他线程唤醒或者超时自动唤醒,唤醒之后需要再次竞争synchronized锁才能继续执行
    • sleep是Thread类提供的方法(也不一定要搭配synchronized使用),调用sleep方法进入TIMED_WAITING状态,不会释放锁,时间到了自动唤醒
  • 相关阅读:
    easyExcel生成个性化表格(自定义行高,合并,字体,去网格线),前后端分离开发下,返回错误的JSON数据给前端
    [译]这几个CSS小技巧,你知道吗?
    Dubbo3元数据参考手册
    千峰商城-springboot项目搭建-78-购物车结算-查询购物车记录接口
    化学制药行业数字化供应链管理平台:精准数据采集,助力产业数字化升级
    Web:体验原生的组件化开发
    机器学习简介
    MATLAB中polyfit函数使用
    DBCO-C12-amine,DBCO-C12-NH2,二苯并环辛炔-碳12-氨基, DBCO-C12-胺
    如何使用前端桌面应用程序框架(Electron等)?
  • 原文地址:https://blog.csdn.net/qq_50985215/article/details/125493497