在Java中,wait()
和notify()
/notifyAll()
方法是用于多线程编程中线程间通信的重要机制,它们与对象的监视器(monitor)紧密相关。下面,我们将从技术难点、面试官关注点、回答吸引力和程序举例四个方面,详细解析这两个方法的工作原理。
一、技术难点
-
监视器锁:理解
wait()
和notify()
方法的前提是理解Java中的监视器锁。当一个线程进入一个对象的synchronized
代码块或方法时,它会自动获得该对象的监视器锁。只有持有该锁的线程才能调用该对象的wait()
或notify()
/notifyAll()
方法。 -
等待队列和通知队列:在Java内部,每个对象都有一个与之关联的等待队列(wait set)和一个通知队列(entry set)。当线程调用
wait()
方法时,它会被放入该对象的等待队列中,并释放监视器锁。当其他线程调用notify()
或notifyAll()
方法时,它会从等待队列中唤醒一个或所有线程,并将它们放入通知队列中。被唤醒的线程不会立即执行,它们需要再次获得监视器锁后才能继续执行。 -
假唤醒:尽管
wait()
方法通常会使线程进入等待状态,但Java规范允许在没有其他线程调用notify()
或notifyAll()
方法的情况下,等待的线程也可以被唤醒,这被称为假唤醒。因此,在编写使用wait()
和notify()
的代码时,通常需要将wait()
方法放在一个循环中,以检查等待的条件是否已满足。
二、面试官关注点
-
对监视器锁的理解:面试官通常会询问你对Java监视器锁的理解,以及它是如何与
wait()
和notify()
方法一起工作的。 -
线程间的通信机制:面试官会关注你是否了解线程间通信的基本机制,包括如何使用
wait()
和notify()
/notifyAll()
方法来实现线程间的通信。 -
避免死锁和活锁:在使用
wait()
和notify()
方法时,很容易陷入死锁或活锁的情况。面试官会关注你是否了解这些潜在问题,并知道如何避免它们。
三、回答吸引力
-
清晰明了:用简洁明了的语言解释
wait()
和notify()
方法的工作原理,避免使用过于复杂的术语或冗长的句子。 -
实例支持:通过具体的编程实例来展示
wait()
和notify()
方法的使用方法和注意事项,这样可以让回答更加生动和直观。 -
深入分析:不仅解释基本概念,还要深入分析它们的工作原理、潜在问题和解决方案。
四、程序举例
下面是一个简单的Java程序示例,演示了如何使用wait()
和notify()
方法实现线程间的通信:
java复制代码
public class SharedResource { | |
private int count = 0; | |
public synchronized void increment() throws InterruptedException { | |
while (count == 5) { | |
wait(); // 当前线程等待,释放锁 | |
} | |
count++; | |
System.out.println("Count is " + count); | |
notify(); // 唤醒等待的线程 | |
} | |
public synchronized void decrement() throws InterruptedException { | |
while (count == 0) { | |
wait(); // 当前线程等待,释放锁 | |
} | |
count--; | |
System.out.println("Count is " + count); | |
notify(); // 唤醒等待的线程 | |
} | |
} | |
// ... 省略了使用SharedResource类的线程代码 |
在这个示例中,SharedResource
类中的increment()
和decrement()
方法都是synchronized
的,这意味着它们在同一时间只能被一个线程访问。当count
的值达到某个特定值时(如5或0),线程会调用wait()
方法进入等待状态,并释放监视器锁。其他线程可以通过调用notify()
方法唤醒等待的线程。通过这种方式,我们可以实现线程间的通信和同步。