Java多线程中sleep和wait区别

 更新时间:2023年06月06日 10:54:15   作者:逆流°只是风景-bjhxcc  
本文主要介绍了Java多线程中sleep和wait区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

sleep(休眠) 和 wait(等待) 方法是 Java 多线程中常用的两个方法,它们有什么区别及一些该注意的地方有哪些呢?下面给大家一一分解。

sleep和wait方法都是native关键字修饰的方法,这说明这两个方法是原生函数,也就是由C/C++实现的,那么我们就暂时不关心它的具体实现了。

sleep方法是Thread类中的方法,而wait方法是Object中的方法,那么我们首先看看wait方法。

wait()

从Object源码中,我们可以发现,wait有三个重载方法,分别是无参的wait方法,带有long和int类型参数的的wait方法,以及带有long类型参数的方法。其实前两个方法最终都是调用了wait(long)方法,而wait(long)方法是native修饰的方法,它底层就是由C++实现的,这里暂且不讨论,我们来看看这个方法的注释。

/*
wait方法会导致当前线程等待,直到其他线程调用notify和notifyAll方法,或者达到了指定的等待时间
使用wait方法的前提是当前线程拥有该对象的监视器也就是锁
该方法会导致当前线程T(调用wait方法的线程)将自己放到该对象的等待集合中,然后会放弃此对象上的所有同步声明,也就是会放弃对象的锁
该等待线程不会被调度并且处于休眠状态,直到以下四种情况之一发生:
1、其他线程调用等待对象notify方法,并且当前线程T被随机选为要唤醒的方法时,线程将会退出休眠状态
2、其他线程调用等待对象的notifyAll方法
3、其他线程中断当前线程T
4、超过指定的等待时间。如果等待时间为0的话,时间因素将不会被考虑,那线程将等待直到被通知唤醒
当发生上述四种情况时,线程T将会从该对象的等待集合中移除,并且可以重新被调度。然后它以通常的方式与其他线程竞争对象上的同步锁,一旦它获得了对对象的控制权,它对对象的所有同步声明将会恢复到原来的状态,也就是说,恢复到调用wait方法时的状态。然后线程T将会从调用wait方法的方法中返回。因此,从wait方法返回时,对象和线程T的同步状态与调用wait方法时的状态完全相同。
如果当前线程在等待之前或者等待时被中断,会抛出InterruptedException异常。
注意,等待方法将当前线程防止到该对象的等待集合时,只解锁此对象;在线程等待时,当前线程同步的其他任何对象都将保持锁定状态。
wait方法仅能被持有对象监视器的线程调用(对象监视器就相当于对象的锁)
通过如下方法可以获得对象的监视器:
1、通过执行该对象的同步方法(也就是synchronized关键字修饰的方法)
2、通过执行该对象的同步代码块(synchronized(Object) {})
3、通过执行类的同步静态代码块(也就是synchronized关键字修饰的静态方法)
*/
public final native void wait(long timeout) throws InterruptedException;

 通过wait方法的注释,我们可以发现,wait方法有如下作用:

  • 使线程进入休眠状态,不被调度,直到被notify方法选中或者notifyAll方法的执行,才会被唤醒
  • 线程会释放调用wait方法的对象的锁(但是不会释放线程持有的其他对象的锁),这样其他线程可以竞争该对象的锁
  • 从wait方法中退出后,线程会回到调用该方法时的状态

既然要notify和notifyAll方法才能唤醒调用wait方法陷入等待的线程,那么我们看看这两个方法的注释:

/*
唤醒一个等待对象锁的线程
如果有多个线程在等待该对象,会随机唤醒一个线程
在当前线程放弃该对象的锁之前,唤醒的线程将无法继续执行
唤醒的线程将以通常的方式与其他线程竞争,这些线程会公平地在这个对象上进行同步竞争。例如,被唤醒的线程在竞争对象的锁时没有特权或者缺点
该方法仅在线程持有对象的监视器时才能被调用,获取对象的监视器有如下方法:也就是synchronized关键字修饰的方法、静态方法以及代码块等
*/
public final native void notify();
/*
唤醒所有等待该对象监视器的线程
在当前持有对象锁的线程放弃对象锁之前,被唤醒的线程无法执行
*/
public final native void notifyAll();

通过方法的注释来看,这两个方法就是用于唤醒等待该对象的线程,notify随机唤醒一个线程,notifyAll会唤醒全部线程,这些被唤醒的线程处于就绪态,它们会和正在运行的线程一起抢占对象的锁,得到对象的锁之后才能继续执行。

通过JDK源码的注释,我们对wait和notify方法有了更进一步的了解,那么接着看看sleep方法,才能知道wait和sleep的区别

sleep()

Thread类中的sleep方法也有两个重载方法,其中sleep(long)是底层的实现。

来看看它们的注释:

/*
根据系统计时器和调度程序的精度和准确性,使当前执行的线程休眠(暂时停止执行)指定的毫秒数
线程不会释放持有的锁
该方法响应中断,当遇到中断时会抛出InterruptedException异常,并且会清除当前线程的中断状态
*/
public static native void sleep(long millis) throws InterruptedException;
/*
使当前执行的线程休眠(临时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统计时器和调度程序的精度和准确性
线程不会释放持有的锁
nanos的范围:0-999999
*/
public static void sleep(long millis, int nanos)
    throws InterruptedException {
        // 判断millis和nanos是否符合条件
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }
        // 调用原生sleep方法
        sleep(millis);
    }

wait()和sleep()方法区别

区别1:使用限制

使用 sleep 方法可以让让当前线程休眠,时间一到当前线程继续往下执行,在任何地方都能使用,但需要捕获 InterruptedException 异常。

try {
    Thread.sleep(3000L);
} catch (InterruptedException e) {
    e.printStackTrace();
}

而使用 wait 方法则必须放在 synchronized 块里面,同样需要捕获 InterruptedException 异常,并且需要获取对象的锁。

synchronized (lock){
    try {
        lock.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

而且 wait 还需要额外的方法 notify/ notifyAll 进行唤醒,它们同样需要放在 synchronized 块里面,且获取对象的锁。。

synchronized (lock) {
    // 随机唤醒
    lock.notify();
    // 唤醒全部
    lock.notifyAll();
}

当然也可以使用带时间的 wait(long millis) 方法,时间一到,无需其他线程唤醒,也会重新竞争获取对象的锁继续执行。

区别2:使用场景

sleep 一般用于当前线程休眠,或者轮循暂停操作,wait 则多用于多线程之间的通信。

区别3:所属类

sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。

java.lang.Thread#sleep

public static native void sleep(long millis) throws InterruptedException;

java.lang.Object#wait

public final native void wait(long timeout) throws InterruptedException;

为什么要这样设计呢?

因为 sleep 是让当前线程休眠,不涉及到对象类,也不需要获得对象的锁,所以是线程类的方法。wait 是让获得对象锁的线程实现等待,前提是要楚获得对象的锁,所以是类的方法。

区别4:释放锁

Object lock = new Object();
synchronized (lock) {
    try {
        lock.wait(3000L);
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

如上代码所示,wait 可以释放当前线程对 lock 对象锁的持有,而 sleep 则不会。

区别5:线程切换

sleep 会让出 CPU 执行时间且强制上下文切换,而 wait 则不一定,wait 后可能还是有机会重新竞争到锁继续执行的。

到此这篇关于Java多线程中sleep和 wait区别的文章就介绍到这了,更多相关Java sleep和wait内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java基于Socket做一个简单下载器

    java基于Socket做一个简单下载器

    这篇文章主要为大家详细介绍了java如何基于Socket制作一个简单下载器,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • SpringCloud微服务开发基于RocketMQ实现分布式事务管理详解

    SpringCloud微服务开发基于RocketMQ实现分布式事务管理详解

    分布式事务是在微服务开发中经常会遇到的一个问题,之前的文章中我们已经实现了利用Seata来实现强一致性事务,其实还有一种广为人知的方案就是利用消息队列来实现分布式事务,保证数据的最终一致性,也就是我们常说的柔性事务
    2022-09-09
  • java 流与 byte[] 的互转操作

    java 流与 byte[] 的互转操作

    这篇文章主要介绍了java 流与 byte[] 的互转操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • mac下修改idea的jvm运行参数解决idea卡顿的情况

    mac下修改idea的jvm运行参数解决idea卡顿的情况

    这篇文章主要介绍了mac下修改idea的jvm运行参数解决idea卡顿的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java事务回滚详解以及常见误区

    Java事务回滚详解以及常见误区

    事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消,这篇文章主要介绍了Java事务回滚详解以及常见误区的相关资料,需要的朋友可以参考下
    2025-09-09
  • 聊一聊concurrenthashmap的size方法原理

    聊一聊concurrenthashmap的size方法原理

    这篇文章主要介绍了concurrenthashmap的size方法原理,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • 带你重新认识MyBatis的foreach

    带你重新认识MyBatis的foreach

    这篇文章主要介绍了重新认识MyBatis的foreach,本文提出了一种简化<foreach>写法的设想,更重要的是通过解决空集时生成的SQL语法问题,更深刻地理解MyBatis的foreach的生成机制,需要的朋友可以参考下
    2022-11-11
  • SpringBoot项目中如何解决跨域问题的最新方案?

    SpringBoot项目中如何解决跨域问题的最新方案?

    跨域问题是浏览器为了保护用户的信息安全,实施了同源策略(Same-Origin Policy),即只允许页面请求同源(相同协议、域名和端口)的资源,当 JavaScript 发起的请求跨越了同源策略,即请求的目标与当前页面的域名、端口、协议不一致时,浏览器会阻止请求的发送或接收
    2025-03-03
  • Java后端接入微信小程序登录功能(登录流程)

    Java后端接入微信小程序登录功能(登录流程)

    这篇文章主要介绍了Java后端接入微信小程序登录功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • 基于Spring5实现登录注册功能

    基于Spring5实现登录注册功能

    这篇文章主要为大家详细介绍了基于Spring5实现登录注册功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09

最新评论