Java线程同步机制:深入理解wait与notify/notifyAll

5星 · 超过95%的资源 | 7Z格式 | 2KB | 更新于2025-05-01 | 7 浏览量 | 82 下载量 举报
收藏
在Java编程中,多线程同步是保证多线程环境下的数据安全性和操作有序性的重要机制。Java提供了多种同步方式,其中`wait`和`notify`/`notifyAll`是最常用也是最基础的线程协作机制之一。这些方法是`Object`类的本地方法,因此每个Java对象都有能力成为锁对象,并可调用这些方法进行线程间的通信。 ### wait方法 `wait`方法的作用是让当前线程等待,直到其他线程调用了同一个对象的`notify`或`notifyAll`方法。当线程执行到一个对象的`wait`方法时,它会放弃这个对象的锁,并进入等待状态。在等待状态的线程会进入该对象的等待池中,等待其他线程唤醒。 重要的是,`wait`方法必须在同步代码块或同步方法中被调用,否则会抛出`IllegalMonitorStateException`异常。在调用`wait`方法后,线程会释放对象锁,并进入等待状态。直到其他线程调用此对象的`notify`或`notifyAll`方法,处于等待状态的线程才有机会再次被调度进入执行状态。 ### notify方法 `notify`方法用于随机选择一个处于等待池中的线程,将其转移到对象的锁池中,从而允许它在稍后的时间点执行。在调用`notify`之后,当前线程不会立即释放该对象的锁,该线程会继续执行它的剩余同步代码块。当它完成后,锁会被释放,锁池中的线程才有机会获得锁并执行。 `notify`不会保证哪个等待的线程会获得通知,它是随机的。如果有多个线程在等待,只能有一个线程获得通知,其他线程仍然处于等待状态。 ### notifyAll方法 与`notify`方法类似,`notifyAll`会唤醒所有在此对象上等待的线程。与`notify`不同的是,`notifyAll`保证所有等待的线程都会被通知到。但是要注意,虽然所有线程都会被通知到,但是只有一个线程能够获得锁执行,其他的线程会再次进入等待状态直到它们能有机会获得锁。 在使用`notifyAll`时,需要注意的是,如果线程间没有适当的调度和同步机制,可能会导致某些线程无法获取锁而出现死锁的情况。 ### 使用场景 `wait`和`notify`/`notifyAll`通常在以下场景中使用: 1. 生产者-消费者模型:生产者将产品放入缓冲区后,通知消费者产品已经准备好;消费者消费产品后,通知生产者有更多的空间可用。 2. 任务等待/任务唤醒机制:当一个任务需要等待另一个任务的完成,它会调用`wait`方法,而完成的任务则会调用`notify`或`notifyAll`唤醒等待的任务。 3. 线程协作:多个线程在执行过程中需要协同工作,一个线程到达某个执行点时需要等待其他线程到达相同的执行点。 ### 实践注意事项 1. 必须在同步方法或同步代码块中调用`wait`、`notify`或`notifyAll`。 2. 在调用`notify`或`notifyAll`之后,不要忘记释放锁,否则其他线程仍然无法获取到锁,可能导致死锁。 3. 使用`notifyAll`时需要更加谨慎,因为可能会导致过多的线程竞争锁,造成不必要的上下文切换。 4. 在`wait`方法之后,应该使用循环检查条件是否满足,而不仅仅是依赖于`notify`,这是因为`notify`可能在其他条件满足前就被调用了。 ### 代码示例(以synchronizedThread2为例) ```java public class SynchronizedThread2 { public synchronized void waitMethod() { try { System.out.println("线程 " + Thread.currentThread().getName() + " 进入 waitMethod"); wait(); // 释放锁,并使当前线程进入等待状态 System.out.println("线程 " + Thread.currentThread().getName() + " 从 waitMethod 被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void notifyMethod() { notify(); // 随机唤醒一个等待的线程 System.out.println("线程 " + Thread.currentThread().getName() + " 调用了 notify"); } public static void main(String[] args) { SynchronizedThread2 st2 = new SynchronizedThread2(); Thread t1 = new Thread(() -> { st2.waitMethod(); }); Thread t2 = new Thread(() -> { st2.notifyMethod(); }); t1.setName("等待线程"); t2.setName("通知线程"); t1.start(); try { // 为了确保等待线程已经进入等待状态 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } ``` 在上面的代码示例中,我们创建了一个`SynchronizedThread2`类,其中包含`waitMethod`和`notifyMethod`两个同步方法。我们创建了两个线程:`t1`进入`waitMethod`等待,而`t2`执行`notifyMethod`来唤醒等待的线程。注意在启动线程后使用了`Thread.sleep`来确保等待线程`t1`已经进入等待状态。 通过这个示例,我们可以理解`wait`和`notify`/`notifyAll`的工作原理以及它们在实际编程中的应用。这些机制允许我们在多线程应用中更加灵活地控制线程间的协作与同步,是Java并发编程中不可或缺的一部分。

相关推荐