【校招VIP】wait/notify/notifyAll常见面试题解析

08月04日 收藏 0 评论 2 java开发

【校招VIP】wait/notify/notifyAll常见面试题解析

转载声明:原文链接:https://blog.csdn.net/oman001/article/details/105102761  

面试常见问题:

为什么 wait 必须在 synchronized 同步代码中使用?
为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?
wait/notify 和 sleep 方法的异同?

为什么 wait 必须在 synchronized 同步代码中使用?

至于这个问题,其实想一想如果不在synchronized同步代码中使用的话会怎么样,答案就很清楚了。下面通过一个例子来说明这个问题:

class MyQueue {
private final ArrayList<String> list = new ArrayList<>(5);

public void consume() {
while(true) {
if(list.size()==0) {
list.wait();
}
System.out.println(list.remove(0));
list.notify();
}
}

public void product() {
while(true) {
if(list.size()==5) {
list.wait();
}
list.add("element");
list.notify();
}
}
}

在上述伪代码中,类MyQueue中有两个方法,分别提供给两个线程调用,消费者线程调用consume() 方法,生产者线程调用 product() 方法,如果我们不加synchronized关键字加锁的话,下面我们来看一种可能的场景(关于生产者消费者模式可以查看生产者消费者模式详解):

消费者线程在判断if(list.size()==0)的时候成功了,这时候应该调用list.wait()方法来等待其它线程添加元素,但是因为没有添加synchronized,这时候list.wait()方法不一定会执行,可能线程被CPU暂停了。
生产者线程这时候开始执行添加的任务,当添加完元素之后,调用list.notify()的时候想要通知等待的线程,可是因为没有需要等待的线程,因为消费者线程并没有执行wait()方法,所以就可能导致消费者线程出现一直等待的错误。
如果我们对两个代码块都加锁的话,所有的执行就都是原子性的了,就不会出现消费者线程list.size()==0 和 list.wait()中间断开,导致生产者线程的notify()插入其中 的情况了。这样的话程序就安全了。
所以wait 为什么必须在 synchronized 同步代码中使用?就是为了程序的安全性,另外想要调用wait()方法释放锁,也必须先要持有锁对象才可以,否则的话是会报IllegalMonitorStateException异常的。

为什么 wait/notify/notifyAll 被定义在 Object 类中,而 sleep 定义在 Thread 类中?

可能我们司空见惯了wait/notify/notifyAll 在Object类中,直接使用它,但是并不关心它为什么这么设置的原因。针对这个问题,其实也好理解,主要原因如下:
因为Java的每一个对象都有一个锁(monitor 或者监视器),而wait/notify/notifyAll的目的是为了等待其它线程释放持有的对象的锁,或者唤醒其它线程持有对象的锁,而且Java线程中也没有任何可供对象使用的锁和同步器,另外将这些方法定义在Object中,也是为了使得Java的每一个对象都可以用于线程间通信。
因为对象中的锁是对象级别的,而非线程级别的,wait/notify/notifyAll也都是针对锁级别的操作,它们的锁属于对象,所以把它们定义在Object类中是最合适,因为Object类是所有对象的父类。
如果把wait/notify/notifyAll 方法定义在 Thread 类中,我们该如何让一个线程持有多把锁?又如何确定线程等待的是哪一把锁?既然是线程等待某一个对象的锁,那么操作对象是最为合适的,而不是线程。

wait 和 sleep 方法的异同?

在Java线程六种状态图解中我们对线程的六种状态做了详细的分析,这里我们再来总结一下。
相同点:都可以使得线程阻塞,并且都可以在阻塞过程中感受到中断信号,抛出InterruptedException 异常。
不同点:首先从是否释放锁的角度来讲,wait()方法会释放锁,但是sleep()方法会一直持有锁,这就导致wait()/wait(time)方法结束后会将线程从WAITING或者TIME_WAITING状态变为BLOCKED状态,只有再次持有锁线程才会变为RUNNABLE状态,而sleep()方法因为一直持有锁,导致超时之后直接从TIME_WAITING状态变为RUNNABLE状态。
wait方法必须在synchronized同步代码中使用,并且会释放锁,而sleep不需要。
wait方法是Object的方法,sleep是Thread的方法。
sleep方法是必须要求指定一个时间,超时之后就会恢复操作,而wait()方法如果没有设置时间的话, 会一直处于等待状态,只有等到被唤醒或者被中断才会恢复。

C 2条回复 评论
瀑布的背后

云里雾里地听完了……

发表于 2023-09-15 22:00:00
0 0
匀斋

想要学 UI,自学的方法遍地是,网上教程一搜一大堆

发表于 2023-09-02 21:00:00
0 0