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

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

写在开头

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

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

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哥!

目录
相关文章
|
2月前
|
Android开发 Kotlin
Android经典面试题之Kotlin的==和===有什么区别?
本文介绍了 Kotlin 中 `==` 和 `===` 操作符的区别:`==` 用于比较值是否相等,而 `===` 用于检查对象身份。对于基本类型,两者行为相似;对于对象引用,`==` 比较值相等性,`===` 检查引用是否指向同一实例。此外,还列举了其他常用比较操作符及其应用场景。
187 93
|
14天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
1月前
|
编译器
经典面试题:变量的声明和定义有什么区别
在编程领域,变量的“声明”与“定义”是经典面试题之一。声明告诉编译器一个变量的存在,但不分配内存,通常包含变量类型和名称;而定义则为变量分配内存空间,一个变量必须至少被定义一次。简而言之,声明是告知变量形式,定义则是实际创建变量并准备使用。
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
112 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
1月前
|
前端开发 小程序 JavaScript
面试官:px、em、rem、vw、rpx 之间有什么区别?
面试官:px、em、rem、vw、rpx 之间有什么区别?
38 0
|
2月前
|
Java 关系型数据库 MySQL
面试官:GROUP BY和DISTINCT有什么区别?
面试官:GROUP BY和DISTINCT有什么区别?
89 0
面试官:GROUP BY和DISTINCT有什么区别?
【多线程面试题十】、说一说notify()、notifyAll()的区别
notify()唤醒单个等待对象锁的线程,而notifyAll()唤醒所有等待该对象锁的线程,使它们进入就绪队列竞争锁。
|
3月前
|
算法 Java
【多线程面试题十八】、说一说Java中乐观锁和悲观锁的区别
这篇文章讨论了Java中的乐观锁和悲观锁的区别,其中悲观锁假设最坏情况并在访问数据时上锁,如通过`synchronized`或`Lock`接口实现;而乐观锁则在更新数据时检查是否被其他线程修改,适用于多读场景,并常通过CAS操作实现,如Java并发包`java.util.concurrent`中的类。
|
3月前
|
Java
【多线程面试题十三】、说一说synchronized与Lock的区别
这篇文章讨论了Java中`synchronized`和`Lock`接口在多线程编程中的区别,包括它们在实现、使用、锁的释放、超时设置、锁状态查询以及锁的属性等方面的不同点。
【多线程面试题九】、说一说sleep()和wait()的区别
sleep()和wait()的主要区别在于sleep()是Thread类的静态方法,可以在任何地方使用且不会释放锁;而wait()是Object类的方法,只能在同步方法或同步代码块中使用,并会释放锁直到相应线程通过notify()/notifyAll()重新获取锁。