Java中的wait方法是一个非常重要的多线程技术,它可以使线程处于等待状态,直到其他线程调用notify或notifyAll方法唤醒它。wait和notify方法通常用于协调线程之间的操作,以确保多个线程可以正确共享资源。下面从多个方面来详细阐述Java中的wait方法。
一、wait和notify方法的概述
每个Java对象都有一个监视器(monitor),它是用来控制对该对象的访问的。一个线程要想进入某个对象的监视器区域,必须先获得该对象的锁。然后可以通过wait方法来释放该对象的锁并进入等待状态,直到其他线程调用notify或notifyAll方法唤醒它。notify方法会选择一个等待该对象的线程进行唤醒,而notifyAll方法会唤醒所有等待该对象的线程。
public synchronized void wait() throws InterruptedException public final native void notify() public final native void notifyAll()
wait和notify方法只能在同步方法或同步块中使用。如果一个线程在调用wait方法前没有获得该对象的锁,会抛出IllegalMonitorStateException异常。
二、wait方法的阻塞和唤醒
调用wait方法会释放该对象的锁并将线程置于等待状态,此时线程处于阻塞状态,并且不占用CPU资源。如果其他线程获得了该对象的锁并调用notify或notifyAll方法,则该线程将被唤醒并尝试重新获取该对象的锁。
下面是一个使用wait和notify方法实现的线程间通信的示例:
class Monitor { public synchronized void print() { for(int i=0; i<5; i++) { System.out.println("i = " + i); try { // 释放锁并进入等待状态 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void resume() { // 唤醒等待该对象的线程 this.notify(); } } public class ThreadWaitExample { public static void main(String[] args) { Monitor monitor = new Monitor(); // 创建一个线程打印i的值 Thread printer = new Thread(() -> { monitor.print(); }); // 启动打印线程 printer.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // 创建另一个线程唤醒打印线程 Thread resumer = new Thread(() -> { monitor.resume(); }); // 启动唤醒线程 resumer.start(); } }
在这个示例中,Monitor类中的print方法调用了wait方法来释放锁并等待唤醒。resume方法则调用了notify方法来唤醒等待该对象的线程。程序中创建了两个线程:一个打印线程和一个唤醒线程,唤醒线程会在打印线程执行到第二次循环时调用resume方法唤醒打印线程。
三、wait方法与notify方法的应用
1、wait方法和notify方法在线程间通信中的应用
wait方法和notify方法通常用于协调多个线程之间的操作,以确保多个线程可以正确共享资源。在多个线程间共享一个对象的情况下,某个线程在获取该对象的锁后可能需要等待其他线程的操作完成,才能进行接下来的操作。这时,该线程可以调用wait方法释放锁并等待唤醒。其他线程完成操作后,可以调用notify方法来唤醒等待该对象的线程。
2、wait方法和notify方法在线程池中的应用场景
Java中的线程池是一个线程管理机制,它可以在任务到达时分配一个可用的线程进行处理,而不需要创建新的线程。线程池中的每个线程都会占用一些资源,因此需要根据实际的需求来控制线程的数量。
如果线程池中的所有线程都在处理任务,而没有可用的线程来处理新的任务时,可以使用wait和notify方法来实现线程池的阻塞和唤醒。当线程池中没有可用的线程时,调用线程可以调用wait方法释放锁并等待唤醒。当有线程处理完任务后,可以调用notify方法来唤醒等待线程。
下面是一个使用wait和notify方法实现线程池的示例:
class ThreadPool { private int nThreads; private List<Runnable> tasks; private List<WorkerThread> workers; public ThreadPool(int nThreads) { this.nThreads = nThreads; this.tasks = new LinkedList<>(); this.workers = new ArrayList<>(); for(int i=0; i<nThreads; i++) { WorkerThread worker = new WorkerThread(); workers.add(worker); worker.start(); } } public synchronized void execute(Runnable task) { tasks.add(task); notify(); } public synchronized void shutDown() { tasks.clear(); workers.forEach(worker -> worker.shutdown()); notifyAll(); } private class WorkerThread extends Thread { private boolean running = true; @Override public void run() { while(running) { Runnable task = null; synchronized (ThreadPool.this) { while(tasks.isEmpty()) { try { // 等待唤醒 ThreadPool.this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } task = tasks.remove(0); } if(task != null) { task.run(); } } } public void shutdown() { running = false; } } } public class ThreadPoolExample { public static void main(String[] args) { // 创建一个线程池 ThreadPool threadPool = new ThreadPool(10); // 启动若干个任务 for(int i=0; i<100; i++) { final int index = i; threadPool.execute(() -> System.out.println("Task " + index + " is running.")); } // 加入任务队列之后,等待1s停止线程池 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadPool.shutDown(); } }
在这个示例中,ThreadPool类使用wait和notify方法来实现线程池的阻塞和唤醒。execute方法负责将任务添加到任务队列中,并唤醒等待线程。WorkerThread类负责从任务队列中取出任务并执行,如果任务队列为空,则进入等待状态。shutDown方法负责清空任务队列并停止所有的线程。
总结
本文从多个方面对Java中的wait方法进行了详细阐述,包括wait和notify方法的概述、wait方法的阻塞和唤醒、wait方法和notify方法在线程间通信中的应用、wait方法和notify方法在线程池中的应用场景等。wait方法是Java多线程技术中非常重要的一个方法,对于理解多线程编程、掌握线程间通信以及设计线程安全的程序等方面都具有重要的参考价值。