详解Java的notify()方法:唤醒正在等待此对象监视器的单个线程

  • Post category:Java

首先,notify()是Java中的一个线程同步方法,其作用是唤醒在该对象上等待的某个线程。接下来我将给出notify()方法的详细使用攻略,包括其原理、语法、使用注意事项以及两个示例说明。

原理

在Java中,每个对象都有一个与之相关联的monitor,monitor是同步的基本单位,每个Java对象都有一个内部锁,也就是说在一个时间点,最多只有一个线程能够拥有这个锁,并且在有竞争并发请求的情况下,只有一个线程能够命中该锁。当一个线程调用一个对象的wait()方法时,它就进入的该对象的等待池,处于等待状态,直到有其他线程调用相同对象的notify()方法。当调用notify()方法时,会从该对象的等待池中随机选取一个线程来将其唤醒,使其进入到该对象的锁池,然后等待获取该对象的锁。如果此时该对象的锁没有被其他线程占用,那么该线程就可以获取该对象的锁并继续执行,否则该线程又会重新放入到该对象的等待池中。

语法

notify()方法的语法如下:

public final void notify()

注意事项

  1. notify()方法必须在已经获得对象锁的情况下调用,否则会抛出IllegalMonitorStateException异常。
  2. notify()方法并不会释放对象锁,只有当该线程退出synchronized代码段,或者调用wait()方法等待被唤醒时,对象锁才会被释放。
  3. notify()方法只会唤醒对象等待池中的一个线程,并且无法指定唤醒具体的线程。如果需要唤醒多个线程,应该使用notifyAll()方法。

示例说明

示例一:使用wait()、notify()实现线程同步

在下面的示例中,我们定义了一个对象MessageQueue,并且在其之上定义了两个方法:produceMessage()consumeMessage(),分别表示生产者线程和消费者线程。

当生产者线程调用produceMessage()方法时,它会向messageList列表中添加一个元素,然后通过notify()方法通知消费者线程去消费该元素。

当消费者线程调用consumeMessage()方法时,它会检测messageList是否为空,如果为空,则通过wait()方法进入该对象的等待池,等待生产者线程的唤醒,如果messageList不为空,则从中取出元素,然后通知其他生产者线程。

import java.util.*;

public class MessageQueue {

    private List<String> messageList = new ArrayList<>();

    public synchronized void produceMessage(String message) {
        messageList.add(message);
        notify();
    }

    public synchronized String consumeMessage() {
        while (messageList.size() == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String message = messageList.get(0);
        messageList.remove(0);
        notify();
        return message;
    }
}

示例二:使用notify()唤醒线程

在下面的示例中,我们定义了两个线程,分别为SleepThreadNotifyThread。在SleepThread中,我们让该线程先睡眠2秒钟,然后通过notifyAll()方法通知其他等待线程。

NotifyThread中我们调用了sleep()方法休眠1秒钟,然后通过notify()方法唤醒SleepThread线程。

public class SleepThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 开始运行");
        synchronized (this) {
            try {
                System.out.println(Thread.currentThread().getName() + " 等待 2 秒钟");
                wait(2000);
                System.out.println(Thread.currentThread().getName() + " 结束等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class NotifyThread implements Runnable {

    private SleepThread sleepThread;

    public NotifyThread(SleepThread sleepThread) {
        this.sleepThread = sleepThread;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 开始运行");
        synchronized (sleepThread) {
            try {
                Thread.sleep(1000);
                sleepThread.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {

    public static void main(String[] args) {
        SleepThread sleepThread = new SleepThread();
        new Thread(sleepThread, "SleepThread").start();
        new Thread(new NotifyThread(sleepThread), "NotifyThread").start();
    }
}

以上是关于Java的notify()方法的完整攻略及两条示例说明。希望对你有所帮助。