java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java sleep和wait

Java多线程中sleep和wait区别

作者:逆流°只是风景-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方法陷入等待的线程,那么我们看看这两个方法的注释:

/*
唤醒一个等待对象锁的线程
如果有多个线程在等待该对象,会随机唤醒一个线程
在当前线程放弃该对象的锁之前,唤醒的线程将无法继续执行
唤醒的线程将以通常的方式与其他线程竞争,这些线程会公平地在这个对象上进行同步竞争。例如,被唤醒的线程在竞争对象的锁时没有特权或者缺点
该方法仅在线程持有对象的监视器时才能被调用,获取对象的监视器有如下方法:也就是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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文