您的位置:

JavaNotify:实现线程间通信的利器

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机制。