从Guarded Block来看Java中的wait和notify方法
发布网友
发布时间:2024-10-09 17:24
我来回答
共1个回答
热心网友
时间:2024-10-22 03:42
Java线程的生命周期
本文将深入探讨Java中最基础的机制之一:线程同步。我们将首先回顾一些并发相关的术语和方法论,并通过一个简单示例来处理并发问题,以更好地理解wait()和notify()方法。
多线程环境下,每个线程都有可能修改相同资源,如果线程管理不当,可能会引发并发问题。多线程之间需要协同工作,最常见的方式是使用保护块(Guarded Blocks)。它循环检查一个条件,直到条件发生变化才跳出循环继续执行。
然而,不停地检查循环条件是一种资源浪费,更高效的做法是调用Object.wait将当前线程挂起,直到有另一线程发起事件通知。今天,我们将讨论wait()和notify()方法。
补充:下面的图表展示了wait()和notify()方法在Java线程生命周期中的作用:
可以看到,控制线程生命周期有许多方式,本文仅关注wait()和notify()方法。
wait()方法
调用wait()方法时,当前线程释放锁并挂起。另一个线程请求并获得这个锁,然后调用Object.notifyAll()通知所有等待该锁的线程(之后当前线程释放该锁),此时第一个线程收到通知获取到该锁,从wait()方法返回并继续执行。
wait()方法有三个重载版本:
wait():会使当前线程无限期等待,直到另一个线程调用了当前对象的notify()或notifyAll()方法。
wait(long timeout)和wait(long timeout, int nanos):为等待设置了时间限制,后者提供了更高的精度。
notify()和notifyAll()
notify()方法唤醒等待对象内置锁的线程,有两种方式:
notify():只会唤醒一个线程,适用于大量相似任务的多线程环境,因为并不关心哪一个线程被唤醒。
当前线程必须拥有对象的内置锁或监视器锁,根据Java文档,可通过以下三种方式之一:
注意,同一时间只有一个活跃线程能获取到对象的内置锁。
notifyAll():唤醒所有等待内置锁的线程。被唤醒的线程继续执行直到完成任务。但在唤醒线程开始执行逻辑之前,通常会定义一个快速检查,以确定继续执行所需的条件,因为可能会出现被唤醒的线程没有收到通知的情况。
生产者-消费者同步问题
理解上述内容后,我们来看一个简单的生产者-消费者示例:
我们首先创建一个Drop类,用于生产者向消费者传递数据。我们将使用wait()和notifyAll()方法来让两个线程共享数据。
将wait()方法放入while语句中是因为线程唤醒后当前方法的循环条件可能未改变。同步put()和take()方法是因为在调用wait之前,线程必须拥有对象的内部锁(否则会抛出异常),可以通过在synchronized方法中调用wait()来获得d的内部锁。
接下来,我们将实现Producer和Consumer。
对于Producer:
下面是Consumer的实现:
实现很简单,通过在for循环中调用drop.take()方法,直到收到最后一个数据。
我们现在运行程序:
程序输出如下:
可以观察到,我们以正确的顺序收到了所有消息数据,并成功在Producer和Consumer之间实现了数据共享。
总结
本文讨论了Java的核心概念,特别是如何使用wait()和notify()方法解决同步问题,并通过一个简单示例说明了这些概念的使用。
值得注意的是,这些是低层次的API(wait、notify、notifyAll)。在某些情况下,使用JDK中的Lock、Condition等更高层次的API可能更简单且更方便。关于这些API的更多信息,可以参考相关文章。
测试代码
参考资料