多线程下 wait、notify、park、unpark 和 await、signal 的区别

1、所属类和包:

  • waitnotify 属于 Object 类,用于基本的对象监视器机制,而且它们必须在同步块或同步方法中使用。
  • parkunpark 属于 sun.misc.Unsafe 类,提供了更底层的线程挂起和恢复操作。它们可以在任何地方使用,不需要在同步块中。
  • awaitsignal 属于 java.util.concurrent.locks.Condition 接口,通常在 ReentrantLock 的锁保护下使用。它们提供了更高级别的、更灵活的线程协作机制。

2、唤醒方式:

  • notify 只能唤醒等待在同一对象监视器上的一个线程。(调用notify方法而没有任何等待的线程会引发IllegalMonitorStateException异常)
  • notifyAll 唤醒等待在同一对象监视器上的所有线程。(调用 notifyAll方法而没有任何等待的线程是不会引发异常的)
  • unpark 可以指定唤醒哪个线程,因为需要传递线程对象作为参数。(当无park时执行unpark 不会异常,可以先执行unpark再执行park)
  • signal 只能唤醒等待在相同 Condition 对象上的一个线程(signal 唤醒等待在相同 Condition 对象上的所有线程)。
  • awaitsignal 可以通过使用多个 Condition 对象来实现更灵活的唤醒策略。

3、用法示例:

wait 和 notify:

synchronized (lock) {
    while (conditionIsNotMet()) {
        lock.wait();
    }
    // 执行满足条件时的操作
    lock.notify();
}

park 和 unpark:

import java.util.concurrent.locks.LockSupport;

public class ParkUnparkExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " - Before park");
            // 调用park,线程将被阻塞
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + " - After park");
        });

        // 启动线程
        thread.start();

        try {
            Thread.sleep(2000); // 主线程等待一段时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " - Before unpark");
        // 调用unpark,唤醒指定线程
        LockSupport.unpark(thread);
        System.out.println(Thread.currentThread().getName() + " - After unpark");
    }
}

await 和 signal:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AwaitSignalExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean conditionMet = false;

    public void waitForCondition() throws InterruptedException {
        lock.lock();
        try {
            while (!conditionMet) {
                System.out.println(Thread.currentThread().getName() + " - Waiting for condition");
                condition.await();
            }
        
            System.out.println(Thread.currentThread().getName() + " - Condition is met");
        } finally {
            lock.unlock();
        }
    }

    public void signalCondition() {
        lock.lock();
        try {
       
            System.out.println(Thread.currentThread().getName() + " - Signaling condition");
            conditionMet = true;
            condition.signal(); 
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        AwaitSignalExample example = new AwaitSignalExample();

        Thread waitingThread = new Thread(() -> {
            try {
                example.waitForCondition();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "WaitingThread");

        Thread signalingThread = new Thread(example::signalCondition, "SignalingThread");

        waitingThread.start();

        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        signalingThread.start();
    }
}