Java是目前最流行的编程语言之一,其并发编程能力也是相当强大的。在多线程编程中,线程间的通信无疑是非常关键的。Java提供了多种方式来实现线程间通信,其中最常见的方式之一就是使用对象的wait()和notify()方法。这种方式不仅简单易用,而且在Java中广泛应用于各种场景下。
一、wait-notify机制的概述
wait-notify机制是Java中实现线程间通信的一种机制。它通过在不同的线程之间共享对象来实现线程间的通信。具体来说,某个线程调用对象的wait()方法后,会让该线程处于等待状态,并且该线程释放所持有的对象锁;而另一个线程调用该对象的notify()方法后,会使得等待中的线程被唤醒并且重新竞争对象锁。因此,wait-notify机制可以帮助我们更好地控制线程的执行。
二、wait-notify机制的使用
1. wait()方法
java.lang.Object提供了wait()方法,用于将当前的线程置入“睡眠”状态,且只有等待另一个线程来唤醒此线程。wait()方法常用于线程间交互/通信的场合,通常有线程A调用了对象的wait()方法进入等待状态,而线程B调用了相同对象的notify()或notifyAll()方法,使得该对象的等待线程重新进入可运行状态。
/**
* wait方法
*/
public class WaitDemo implements Runnable{
private static Object object = new Object();
@Override
public void run() {
synchronized (object){
try {
System.out.println(Thread.currentThread().getName() + " wait开始..");
object.wait();
System.out.println(Thread.currentThread().getName() + " 被唤醒..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo1 = new WaitDemo();
WaitDemo waitDemo2 = new WaitDemo();
Thread thread1 = new Thread(waitDemo1, "线程1");
Thread thread2 = new Thread(waitDemo2, "线程2");
thread1.start();
thread2.start();
System.out.println("主线程睡眠2秒开始..");
Thread.sleep(2000); //主线程睡眠3秒
System.out.println("主线程睡眠2秒结束..");
synchronized (object){
object.notifyAll();
}
System.out.println("主线程唤醒等待线程结束..");
}
}
注意事项:
- wait方法必须使用在synchronized同步中。
- wait方法会释放锁,而notity方法不会释放锁。
- wait方法和notify,notifyAll方法不会自动释放锁。
2. notify()方法
java.lang.Object提供了notify()方法,唤醒一个因调用了wait()方法而处于阻塞状态的线程。
/**
* notify方法
*/
public class NotifyDemo implements Runnable{
private static Object object = new Object();
@Override
public void run() {
synchronized (object){
System.out.println(Thread.currentThread().getName() + " 进入等待状态..");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 被唤醒..");
}
}
public static void main(String[] args) {
NotifyDemo notifyDemo = new NotifyDemo();
Thread thread1 = new Thread(notifyDemo, "线程1");
Thread thread2 = new Thread(notifyDemo, "线程2");
thread1.start();
thread2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
object.notify(); //随机唤醒一个线程
//object.notifyAll(); 唤醒所有线程
}
System.out.println("主线程唤醒所有线程结束..");
}
}
三、wait-notify机制的注意事项
1. Wait和Notify的阻塞队列
线程在争取到锁之后,如果发现自己无法继续执行,就会释放锁,并进入该锁对象的等待队列(wait-set)中。在锁被释放的同时,该线程也进入到该锁的等待队列中,并且会挂起线程。当锁被另一个线程重新竞争并成功获取后,它就会唤醒该锁等待队列中的某个线程,唤醒的操作由notify或者notifyAll方法完成。
2. 避免因过多的notify()而导致の虚假唤醒(Spurious Wakeup)问题
由于操作系统底层的原因,某些情况下JVM会在没有notify()调用的情况下自动唤醒waiting线程。例如:CPU资源不足,只要有线程有等待状态结束了,它就会进行唤醒操作。
为了避免这种情况的发生,往往需要将在wait()的时候用while来判断条件,以保证线程在唤醒后,会重新判断条件并决定是否执行后续的工作。以下是一个示例代码:
/**
* 避免虚假唤醒问题
*/
public class WaitNotifyDemo implements Runnable{
private static final Object object = new Object();
private static boolean flag;
@Override
public void run() {
synchronized (object){
while (!flag){
try {
System.out.println(Thread.currentThread().getName() + "等待中..");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "被唤醒..");
}
}
public static void main(String[] args) {
WaitNotifyDemo waitNotifyDemo = new WaitNotifyDemo();
Thread thread1 = new Thread(waitNotifyDemo, "线程1");
Thread thread2 = new Thread(waitNotifyDemo, "线程2");
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
flag = true;
System.out.println("flag = true,唤醒所有线程..");
object.notifyAll();
}
}
}
总结
本文介绍了Java中实现线程间通信的利器——wait-notify机制。wait-notify机制通过在不同的线程之间共享对象来实现线程间的通信,使用起来简单易懂,适用于各种场景。然而在使用过程中,需注意wait-notify机制的细节和注意事项。我们通过编写示例代码,希望能帮助读者更好地理解wait-notify机制。