2024-03-23  阅读(0)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1774327359

sleep()wait() 两个方法都是用来暂停线程,具备同样的功能,但是他们之间还是存在蛮大的区别的。

区别一:所属类不同

  • sleep()Thread 类的一个静态方法。位于 Thread 中,强调了它是线程中的一部分,代表了线程的一种行为方式,不涉及线程间的交互。
  • wait()Object 类的一个方法。在 Java 中一切皆对象,而所有对象都可以作为一个监视器(monitor),将 wait() 放在 Object 类中,体现的是面向对象设计的原则,是一种线程间通过共享对象进行通信的机制。

区别二:使用语法不同

sleep()Thread 类的一个静态方法,可以随时随地用。而 wait() 必须配合 synchronized 一起使用,不然在运行时就会抛出 IllegalMonitorStateException 的异常,如下:

public class WaitTest {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("执行 sleep(1000) 后...");
        Object lock = new Object();
        lock.wait();
    }
}

编译是不会有问题的,但是执行就会报错:

这个报错的意思是,一个线程在没有持有对象监视器(monitor) 的情况下就调用了该对象的 wait(), notify(), 或 notifyAll() 。所以,一个线程必须要是对象监视器的持有者才能调用这些方法。

区别三:释放锁资源不同

  • sleep() 会让出 CPU 时间,让其他线程有机会执行,但是它不会释放锁资源。
public class SleepTest {
    public synchronized void test() {
        System.out.println(Thread.currentThread().getName() + "-获取锁,time:" + LocalTime.now());

        try {
            Thread.sleep(2000);

            System.out.println(Thread.currentThread().getName() + "-休眠完成,释放锁,time:" + LocalTime.now());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        SleepTest sleepTest = new SleepTest();

        new Thread(sleepTest::test).start();
        new Thread(sleepTest::test).start();
    }
}

结果:

线程 Thread-0 和 线程 Thread-1 竞争同一个锁资源 SleepTest 对象。当 Thread-0 进入休眠后,Thread-1 并没有获取到锁资源进入同步方法,而是等 Thread-0被唤醒后释放锁资源才进入同步方法的。所以,sleep() 暂时时并没有释放锁资源。

  • 当线程调用 wait() 时,它会释放它所持有的锁,允许其他线程进入同步代码块或同步方法。
public class WaitTest {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "-成功获取锁,time:" + LocalTime.now());
                long i = 1;
                while (i < 9999999999L) {
                    i++;
                }
                try {
                    System.out.println(Thread.currentThread().getName() + "-业务执行完成,调用 wait() ,time:" + LocalTime.now());
                    lock.wait(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

        // 等待线程启动获取锁资源
        Thread.sleep(500);
        System.out.println(Thread.currentThread().getName() + "-尝试获取锁,time:" + LocalTime.now());
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + "-成功获取锁,time:" + LocalTime.now());
        }
    }
}

执行结果

Thread-016:53:05 成功获取锁,这时main16:53:05开始获取锁,由于锁已经被 Thread-0 获取了,所以它一直都在那里等待,在 16:53:10Thread-0 终于执行业务完成,调用 wait() 等待 2 秒,而这时 main 成功获取锁,如果 wait() 不释放锁的话,main 获取锁的时间应该是 16:53:12

区别四:唤醒方式不同

调用 sleep() 我们需要传入一个参数,这个参数就表示该线程的休眠时间,当休眠时间一到,它会自动被唤醒。而 wait() 被调用后,它会进入对象的等待集(_WaitSet),它会无限期地等待,直到其他线程调用 notify()notifyAll() 时被唤醒,注意调用 notify() 时,它是随机唤醒线程的,所以调用notify() 后,它还不一定会被唤醒。

当然 wait() 也有一个重载的方法 wait(long timeout),这个方法允许我们传入一个时间单位。

区别五:线程状态不一样

直接看示例:

public class WaitTest {
    public static void main(String[] args) throws InterruptedException {
        Object lock1 = new Object();
        Object lock2 = new Object();

        Thread thread1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        thread1.start();

        Thread thread2 = new Thread(() -> {
            synchronized (lock1) {
                try {
                    lock1.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread2.start();

        Thread thread3 = new Thread(() -> {
            synchronized (lock2) {
                try {
                    lock2.wait(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread3.start();

        Thread.sleep(1000);
        System.out.println("sleep(3000) 之后进入状态:" + thread1.getState());
        System.out.println("wait() 之后进入状态:" + thread2.getState());
        System.out.println("wait(3000) 之后进入状态:" + thread3.getState());
    }
}

结果

  • 调用 sleep() 后,线程状态为:TIMED_WAITING
  • 调用 wait() 后,线程状态为:WAITING
  • 调用 wait(3000) 后,线程状态为:TIMED_WAITING

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文