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


    写在开头

    在线程的生命周期中,不同状态之间切换时,可以通过调用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

  • 相关阅读:
    软考高级软件架构师学习笔记二(软件工程)
    创新案例|市值$400亿的设计平台Canva的增长策略
    网络安全(黑客)自学
    阿里云服务器购买之后设置密码、安全组、增加带宽、挂载云盘教程
    【win10和win11打印机局域网共享】
    你不知道的Python容器
    Vue响应式数据的实现原理(手写副作用函数的存储和执行过程)
    Java 泛型概念与优势(一)
    《Mybatis 手撸专栏》第6章:数据源池化技术实现
    二叉树(tree)相关数据结构和算法
  • 原文地址:https://www.cnblogs.com/JavaBuild/p/18074175