跳转至

Java 中的等待线程(Wait Thread)

简介

在 Java 多线程编程中,wait 线程机制是一项关键特性,它允许线程之间进行有效的通信和同步。通过 wait 方法,一个线程可以暂停执行,释放对象的锁,并等待其他线程的通知。这在多个线程需要协调工作、共享资源或按照特定顺序执行的场景中非常有用。本文将深入探讨 Java 中 wait 线程的基础概念、使用方法、常见实践以及最佳实践。

目录

  1. 基础概念
  2. 使用方法
    • 调用 wait 方法
    • 调用 notifynotifyAll 方法
  3. 常见实践
    • 生产者 - 消费者模式
    • 线程同步控制
  4. 最佳实践
    • 避免死锁
    • 合理使用 waitnotify
  5. 小结
  6. 参考资料

基础概念

在 Java 中,每个对象都有一个内部锁(也称为监视器锁)。当一个线程访问对象的同步方法或同步代码块时,它会自动获取该对象的锁。wait 方法是 Object 类的成员方法,它只能在同步代码块或同步方法中调用。当一个线程调用对象的 wait 方法时,它会释放该对象的锁,并进入等待状态,直到其他线程调用该对象的 notifynotifyAll 方法。

notify 方法随机唤醒在此对象监视器上等待的单个线程。notifyAll 方法则唤醒在此对象监视器上等待的所有线程。唤醒的线程将竞争对象的锁,获取锁后继续执行。

使用方法

调用 wait 方法

下面是一个简单的示例,展示如何在同步代码块中调用 wait 方法:

public class WaitExample {
    public static void main(String[] args) {
        Object lock = new Object();

        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("等待线程开始执行");
                try {
                    lock.wait();
                    System.out.println("等待线程被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        waitingThread.start();

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

        Thread notifyingThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("通知线程开始执行");
                lock.notify();
                System.out.println("通知线程发出通知");
            }
        });

        notifyingThread.start();
    }
}

在这个示例中,waitingThread 进入同步代码块后调用 lock.wait(),释放 lock 的锁并进入等待状态。notifyingThread 在延迟 2 秒后启动,进入同步代码块并调用 lock.notify(),唤醒 waitingThread

调用 notifynotifyAll 方法

notify 方法只唤醒一个等待线程,而 notifyAll 方法唤醒所有等待线程。下面是一个使用 notifyAll 的示例:

public class NotifyAllExample {
    public static void main(String[] args) {
        Object lock = new Object();

        Thread waitingThread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("等待线程 1 开始执行");
                try {
                    lock.wait();
                    System.out.println("等待线程 1 被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread waitingThread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("等待线程 2 开始执行");
                try {
                    lock.wait();
                    System.out.println("等待线程 2 被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        waitingThread1.start();
        waitingThread2.start();

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

        Thread notifyingThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("通知线程开始执行");
                lock.notifyAll();
                System.out.println("通知线程发出通知");
            }
        });

        notifyingThread.start();
    }
}

在这个示例中,waitingThread1waitingThread2 都调用 lock.wait() 进入等待状态。notifyingThread 调用 lock.notifyAll() 唤醒这两个等待线程。

常见实践

生产者 - 消费者模式

生产者 - 消费者模式是多线程编程中的经典模式,waitnotify 方法常用于实现该模式。下面是一个简单的生产者 - 消费者示例:

import java.util.LinkedList;
import java.util.Queue;

class ProducerConsumerExample {
    private static final int MAX_SIZE = 5;
    private Queue<Integer> queue = new LinkedList<>();

    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (this) {
                while (queue.size() == MAX_SIZE) {
                    System.out.println("队列已满,生产者等待");
                    wait();
                }

                queue.add(value++);
                System.out.println("生产者生产: " + value);
                notify();
            }
            Thread.sleep(1000);
        }
    }

    public void consume() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (queue.isEmpty()) {
                    System.out.println("队列已空,消费者等待");
                    wait();
                }

                int item = queue.poll();
                System.out.println("消费者消费: " + item);
                notify();
            }
            Thread.sleep(1000);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        ProducerConsumerExample pc = new ProducerConsumerExample();

        Thread producerThread = new Thread(() -> {
            try {
                pc.produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumerThread = new Thread(() -> {
            try {
                pc.consume();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

在这个示例中,生产者线程在队列满时调用 wait 等待,消费者线程在队列空时调用 wait 等待。当生产者生产数据或消费者消费数据时,调用 notify 唤醒对方线程。

线程同步控制

waitnotify 方法还可以用于控制线程的执行顺序。例如,让一个线程等待另一个线程完成特定操作后再继续执行:

public class ThreadSyncExample {
    private static boolean flag = false;

    public static void main(String[] args) {
        Object lock = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程 1 开始执行");
                // 执行一些操作
                flag = true;
                lock.notify();
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程 2 开始执行");
                while (!flag) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程 2 继续执行");
            }
        });

        thread2.start();
        thread1.start();
    }
}

在这个示例中,thread2 等待 thread1flag 设置为 true 并调用 notify 后才继续执行。

最佳实践

避免死锁

死锁是多线程编程中常见的问题,当两个或多个线程相互等待对方释放资源时就会发生死锁。为了避免死锁,应遵循以下原则: - 获取锁的顺序一致:在多个线程中,按照相同的顺序获取锁。 - 避免嵌套锁:尽量减少锁的嵌套使用,简化锁的层次结构。 - 设置合理的超时时间:使用 tryLock 方法并设置超时时间,避免线程无限期等待。

合理使用 waitnotify

  • 使用 while 循环检查条件:在调用 wait 方法前,使用 while 循环检查等待条件,以防止虚假唤醒(即线程在没有被 notifynotifyAll 唤醒的情况下醒来)。
  • 确保锁的一致性:在调用 waitnotifynotifyAll 方法时,确保所有相关线程都使用同一个对象的锁。
  • 避免过度通知:尽量使用 notify 方法唤醒单个线程,只有在需要唤醒所有等待线程时才使用 notifyAll 方法,以提高性能。

小结

本文介绍了 Java 中 wait 线程的基础概念、使用方法、常见实践以及最佳实践。waitnotify 方法是 Java 多线程编程中实现线程通信和同步的重要工具,正确使用它们可以解决许多复杂的多线程问题。在实际应用中,需要注意避免死锁、合理使用 waitnotify,以确保程序的正确性和性能。

参考资料