首页 > 其他分享 >一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!

一张图搞清楚wait、sleep、join、yield四者区别,面试官直接被征服!

时间:2024-03-14 22:36:59浏览次数:21  
标签:面试官 join Thread yield 线程 sleep 方法 wait

写在开头

在线程的生命周期中,不同状态之间切换时,可以通过调用sleep()、wait()、join()、yield()等方法进行线程状态控制,针对这一部分知识点,面试官们也会做做文章,比如问你这些方法的作用以及之间的区别。

那么今天我们就一起来总结一下这几个方法的作用及区别,先画一个思维导图梳理一下,便于理解与记忆,争取在被问到这个点时彻底征服面试官!(图片可保存常看哈
image

sleep()

sleep()是Thread类中的一个静态本地方法,通过设置方法中的时间参数,使调用它的线程休眠指定时间,线程从RUNNING状态转为BLOCKED状态,这个过程中会释放CPU资源,给其他线程运行机会时不考虑线程的优先级,但如果有同步锁则sleep不会释放锁即其他线程无法获得同步锁,需要注意的是sleep()使用时要处理异常。休眠时间未到时,可通过调用interrupt()方法来唤醒休眠线程。

【代码示例1】

try {//sleep会发生异常要显示处理
    Thread.sleep(20);//暂停20毫秒
} catch (InterruptedException e) {
    e.printStackTrace();
}

为什么sleep()放在Thread类中

答:因为sleep是线程级别的休眠,不涉及到对象类,只是让当前线程暂停,进入休眠状态,并不释放同步锁资源,也不需要去获得对象锁。

wait()

wait() 是Object类的成员本地方法,会让持有对象锁的线程释放锁,进入线程等待池中等待被再次唤醒(notify随机唤醒,notifyAll全部唤醒,线程结束自动唤醒)即放入锁池中竞争同步锁,同时释放CPU资源,它的调用必须在同步方法或同步代码块中执行,也需要捕获 InterruptedException 异常。

【代码示例2】

//同步代码块
 synchronized (obj) {
     System.out.println("obj to wait on RunnableImpl1");
     try {
         obj.wait();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     System.out.println("obj continue to run on RunnableImpl1");
 }

为什么wait()是Object的方法

答:每个对象都拥有对象锁,wait的作用是释放当前线程所占有的对象锁,自然是要操作对应的Object而不是Thread,因此wait要放入到Object中。

join()

join()同样是Thread中的一个方法,调用join的线程拥有优先使用CPU时间片的权利,其他线程需要等待join()调用线程执行结束后才能继续执行,探索其底层会发现,它的底层是通过wait()进行实现,因此它也需要处理异常。

【代码示例3】

//创建TestRunnable类
TestRunnable mr = new TestRunnable();
//创建Thread类的有参构造,并设置线程名
Thread t1 = new Thread(mr, "t1");
Thread t2 = new Thread(mr, "t2");
Thread t3 = new Thread(mr, "t3");
//启动线程
t1.start();
try {
    t1.join(); //等待t1执行完才会轮到t2,t3抢
} catch (InterruptedException e) {
    e.printStackTrace();
}
t2.start();
t3.start();

我们跟进join()方法内部会发现,其底层主要通过wait()实现,其中参数代表等待当前线程最多执行 millis 毫秒,如果 millis 为 0,则会一直执行,直至完成,其他线程才会继续向下;

【源码示例1】

    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

yield()

yield()是Thread的一个静态方法,它的调用不需要传入时间参数,并且yield() 方法只会给相同优先级或更高优先级的线程运行的机会,并且调用yield的线程状态会转为就绪状态,调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的线程使用CPU了,没有任何机制保证采纳。所以可能它刚让出CPU时间片,又被线程调度器分配了一个时间片继续执行了。使用时不需要处理异常。

【代码示例4】

public class Test {

    public static void main(String[] args) {
        Thread thread1 = new Thread(Test::printNumbers, "小明");
        Thread thread2 = new Thread(Test::printNumbers, "小华");
        thread1.start();
        thread2.start();
    }
    private static void printNumbers() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);

            // 当 i 是偶数时,当前线程暂停执行
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + " 让出控制权...");
                Thread.yield();
            }
        }
    }
}

输出:

小明: 1
小华: 1
小华: 2
小明: 2
小明 让出控制权...
小华 让出控制权...
小明: 3
小明: 4
小明 让出控制权...
小明: 5
小华: 3
小华: 4
小华 让出控制权...
小华: 5

总结

上文中,我们结合代码示例,对这个四个方法进行了详细介绍,下面我们以sleep()为参照,进行对比总结:

(1)sleep()与wait()的区别?

  1. sleep() 是 Thread 类的静态本地方法;wait() 是Object类的成员本地方法;
  2. JDK1.8 sleep() wait() 均需要捕获 InterruptedException 异常;
  3. sleep() 方法可以在任何地方使用;wait() 方法则只能在同步方法或同步代码块中使用;
  4. sleep() 会休眠当前线程指定时间,释放 CPU 资源,不释放对象锁,休眠时间到自动苏醒继续执行;wait() 方法放弃持有的对象锁,进入等待队列,当该对象被调用 notify() / notifyAll() 方法后才有机会竞争获取对象锁,进入运行状态。

(2)sleep()与yield()的区别?

  1. sleep() 方法给其他线程运行机会时不考虑线程的优先级;yield() 方法只会给相同优先级或更高优先级的线程运行的机会;
  2. sleep() 方法声明抛出 InterruptedException;yield() 方法没有声明抛出异常;
  3. 线程执行 sleep() 方法后进入超时等待状态;线程执行 yield() 方法转入就绪状态,可能马上又得得到执行;
  4. sleep() 方法需要指定时间参数;yield() 方法出让 CPU 的执行权时间由 JVM 控制。

(3)sleep()与join()的区别?

  1. JDK1.8 sleep() join() 均需要捕获 InterruptedException 异常;
  2. sleep()是Thread的静态本地方法,join()是Thread的普通方法;
  3. sleep()不会释放锁资源,join()底层是wait方法,会释放锁。

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

image

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

image

标签:面试官,join,Thread,yield,线程,sleep,方法,wait
From: https://www.cnblogs.com/JavaBuild/p/18074175

相关文章

  • 面试官:Spring Boot 微服务中你使用了哪些 starter maven 依赖项?这些 starter 到底是什
    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点面试官:SpringBoot微服务中你使用了哪些startermaven依赖项在SpringBoot微服务中,可以使用大量的StarterMaven依赖项来简化项目配置和快速集成各种功能,包括......
  • 面试官:说说反射的底层实现原理?
    反射是Java面试中必问的面试题,但只有很少人能真正的理解“反射”并讲明白反射,更别说能说清楚它的底层实现原理了。所以本文就通过大白话的方式来系统的讲解一下反射,希望大家看完之后能真正的理解并掌握“反射”这项技术。1.什么是反射?反射在程序运行期间动态获取类和操纵类的......
  • 面试官:你还有什么想问我的?
    最近找我辅导面试的同学很多,我总结了一些案例,发现很多同学在面试环节,特别是面试官提问完毕的反问环节,经常犯一些比较低级的错误。当然,也不能完全称之为错误,毕竟网上很多所谓的面试教程都是这么教的。但在实际的面试过程中,如果你真的这么问,就可能会影响你的最终面试结果。挑选了......
  • JUC 源码讲解:sleep()
    JUC源码讲解:sleep()抛出问题sleep()能使线程进入TIMED_WAITING状态,那么,在使用sleep()会不会释放锁?被中断了会不会抛出异常?在线程使用sleep()时,CPU会不会被释放?我们在源码中和实战中看看答案吧!查看源码进入sleep()源码,可以看到它是一个native方法,但是注释很详细,让我们......
  • 面试官:Spring Boot 实现全局异常处理应该怎么实现
    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点面试官:SpringBoot实现全局异常处理应该怎么实现在SpringBoot中实现全局异常处理可以帮助我们统一处理应用程序中的异常,提高代码的可维护性和可读性。通常情况......
  • 面试官:说说线程池的工作原理?
    线程池的底层是基于线程和任务队列来实现的,创建线程池的创建方式通常有以下两种:普通Java项目,使用ThreadPoolExecutor来创建线程池,这点《阿里巴巴Java开发手册》中也有说明,如下图所示:Spring项目中,会使用代码可读性更高的ThreadPoolTaskExecutor来创建线程池,虽然它的......
  • 面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会
    写在开头面试官:小伙子请聊一聊Java中的精灵线程?我:什么?精灵线程?啥时候精灵线程?面试官:精灵线程没听过?那守护线程呢?我:守护线程知道,就是为普通线程服务的线程嘛。面试官:没了?守护线程的特点,怎么使用,需要注意啥,Java中经典的守护线程都有啥?我:不知道。。。这的天,面试一个10K的工作,......
  • 面试官让说出8种创建线程的方式,我只说了4种,然后挂了。。。
    写在开头昨天有个小伙伴私信说自己面试挂在了“Java有几种创建线程的方式”上,我问他怎么回答的,他说自己有背过八股文,回答了:继承Thread类、实现Runnable接口、实现Callable接口、使用线程池这四种,但是面试官让说出8种创建方式,他没说出来,面试就挂了,面试官给的理由是:只关注八股文背......
  • 面试官:说说微服务灰度发布的底层实现?
    微服务中的灰度发布(又称为金丝雀发布)是一种持续部署策略,它允许在正式环境的小部分用户群体上先部署新版本的应用程序或服务,而不是一次性对所有用户同时发布全新的版本。这种方式有助于在生产环境中逐步验证新版本的稳定性和兼容性,同时最小化潜在风险,不影响大部分用户的正常使用......
  • 面试官:说说SpringAOP实现原理?
    AOP(Aspect-OrientedProgramming,面向切面编程)是一种编程技术,它允许开发者在不改变现有代码的情况下,增加新的功能或行为,这些功能或行为被称为“切面”。AOP可以通过预编译方式和运行期动态代理的方式来实现,它的主要目的是降低业务逻辑的耦合性,提高程序的可重用性和开发效率。AOP......