likes
comments
collection
share

Java并发编程:等待唤醒机制

作者站长头像
站长
· 阅读数 21

等待通知:wait()/notify()/notifyAll()

这几个方法是Object类中的方法,业务代码中基本不使用。了解即可。

wait()方法

调用该方法的线程会释放共享资源(释放获取的锁),从运行状态进入阻塞状态。wait()方法进行了重载

  • wait():线程进入阻塞状态之后,等待被唤醒。

  • wait(long timeout):进入阻塞状态后,等待一段时间,如果没有被通知唤醒,会自动进入就绪状态。

  • wait(long timeout, int nanos):超时时间为纳秒。

notify()方法

随机唤醒等待队列中等待同一共享资源的1个线程,并使该线程退出等待队列,进入可运行状态。

notifyAll()方法

使所有正在等待队列中等待同一共享资源的全部线程退出等待队列,进入可运行状态。此时优先级最高的那个线程最先执行,但也有可能是随机执行,取决于JVM虚拟机的实现?

相关面试题目

sleep()方法和wait()方法对比

写两个线程,一个线程打印1-52,另一个线程打印A-Z,打印结果为12A34B...5152Z

class Print {

    private int flag = 1;//信号量。当值为1时打印数字,当值为2时打印字母
    private int count = 1;

    public synchronized void printNum() {
        if (flag != 1) {
            //打印数字
            try {
                // wait使当前线程阻塞,前提是必须获得锁,所以只能在synchronized范围内使用
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print(2 * count - 1);
        System.out.print(2 * count);
        flag = 2;
        notify();
    }

    public synchronized void printChar() {
        if (flag != 2) {
            //打印字母
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print((char) (count - 1 + 'A'));
        count++;//当一轮循环打印完之后,计数器加1
        flag = 1;
        notify();
    }
}

public class Test {
    public static void main(String[] args) {
        Print print = new Print();
        new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                print.printNum();
            }
        }).start();
        new Thread(() -> {
            for (int i = 0; i < 26; i++) {
                print.printChar();
            }
        }).start();
    }
}
  • 共同点:两者都可以暂停线程的执行

  • 区别

    • sleep()方法没有释放锁,wait()方法释放了锁

    • wait()通常被用于线程间交互or通信,sleep()通常被用于暂停执行

    • wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep方法执行完成后,线程会自动苏醒,或者也可以使用wait(long timeout)超时后线程会自动苏醒

    • sleep()是Thread类的静态本地方法,wait()是Object类的本地方法。

为什么 wait() 方法不定义在 Thread 中?

wait() 是让获得对象锁的线程实现等待,会自动释放当前线程占有的对象锁。每个对象(Object)都拥有对象锁,既然要释放当前线程占有的对象锁并让其进入 WAITING 状态,自然是要操作对应的对象(Object)而非当前的线程(Thread)。

类似的问题:为什么 sleep() 方法定义在 Thread 中?

因为 sleep() 是让当前线程暂停执行,不涉及到对象类,也不需要获得对象锁。

可以直接调用 Thread 类的 run 方法吗?

这是另一个非常经典的 Java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!

new 一个 Thread,线程进入了新建状态。调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 但是,直接执行 run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

总结:调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

参考文章

  1. 彻底搞懂Java的等待-通知(wait-notify)机制
  2. 这几种等待唤醒机制【面试必问】你知道吗
  3. JavaGuide面试题
转载自:https://juejin.cn/post/7330090272151535642
评论
请登录