没有合适的资源?快使用搜索试试~ 我知道了~
Java多线程编程线程间通信详解.docx
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 48 浏览量
2022-07-02
22:22:09
上传
评论
收藏 244KB DOCX 举报
温馨提示
试读
39页
某些情况下,程序要执行的操作需要满足一定的条件(下文统一将其称之为保护条件)才能执行。在单线程编程中,我们可以使用轮询的方式来实现,即频繁地判断是否满足保护条件,若不满足则继续判断,若满足则开始执行。但在多线程编程中,这种方式无疑是非常低效的。如果一个线程持续进行无意义的判断而不释放CPU,这就会造成资源的浪费;而如果定时去判断,不满足保护条件就释放CPU,又会造成频繁的上下文切换。总之,不推荐在多线程编程中使用轮询的方式。 等待与通知是这样一种机制:当保护条件不满足时,可以将当前线程暂停;而当保护条件成立时,再将这个线程唤醒。一个线程因其保护条件未满足而被暂停的过程就被称为等待,一个线程使得其他线程的保护条件得以满足的时候唤醒那些被暂停的线程的过程就被称为通知。
资源推荐
资源详情
资源评论
Java 多线程编程线程间通信详解
一.等待与通知
某些情况下,程序要执行的操作需要满足一定的条件(下文统一将其称之为保护
条件)才能执行。在单线程编程中,我们可以使用轮询的方式来实现,即频繁地
判断是否满足保护条件,若不满足则继续判断,若满足则开始执行。但在多线程
编程中,这种方式无疑是非常低效的。如果一个线程持续进行无意义的判断而不
释放 CPU,这就会造成资源的浪费;而如果定时去判断,不满足保护条件就释
放 CPU,又会造成频繁的上下文切换。总之,不推荐在多线程编程中使用轮询
的方式。
等待与通知是这样一种机制:当保护条件不满足时,可以将当前线程暂停;而当
保护条件成立时,再将这个线程唤醒。一个线程因其保护条件未满足而被暂停的
过程就被称为等待,一个线程使得其他线程的保护条件得以满足的时候唤醒那些
被暂停的线程的过程就被称为通知。
1.wait
在 Java 平台中,Object.wait 方法可以用来实现等待,下面是 wait 方法的三个重
载方法:
�
void wait(long timeoutMillis)
调用该方法会使线程进入 TIMED_WAITING 状态,当等待时间结束或其
他线程调用了该对象的 notify 或 notifyAll 方法时会将该线程唤醒。
�
�
void wait(long timeoutMillis, int nanos)
这个方法看上去可以精确到纳秒级别,但实际上并不是。如果 nanos 的
值在 0~999999 之间,就给 timeoutMillis 加 1,然后调用
wait(timeoutMillis)。
�
�
void wait()
该方法相当于 wait(0),即永不超时。调用后当前线程会进入 WAITING 状
态,直到其他线程调用了该对象的 notify 或 notifyAll 方法。
�
先通过一张图来介绍 wait 的实现机制:
在上一篇文章中我们了解到 JVM 会为每个对象维护一个入口集(Entry Set)用
于存储申请该对象内部锁的线程。此外,JVM 还会为每个对象维护一个被称为
等待集(Wait Set)的队列,该队列用于存储该对象上的等待线程。当在线程中
调用某个对象(这里我们称之为对象 A)的 wait 方法后,当前线程会释放内部
锁并进入 WAITING 或 TIMED_WAITING 状态,然后进入等待集中。当其他线
程调用对象 A 的 notify 方法后,等待集中的某个线程会被唤醒并被移出等待集。
这个线程可能会马上获得内部锁,也有可能因竞争内部锁失败而进入入口集,直
到获得内部锁。当重新获取到内部锁后,wait 方法才会返回,当前线程继续执行
后面的代码。
由于 wait 方法会释放内部锁,因此在 wait 方法中会判断当前线程是否持有被调
用 wait 方法的对象的内部锁。如果当前线程没有持有该对象的内部锁,JVM 会
抛出一个 IllegalMonitorStateException 异常。因此,wait 方法在调用时当前线
程必须持有该对象的内部锁,即 wait 方法的调用必须要放在由该对象引导的
synchronized 同步块中。综上所述,使用 wait 方法实现等待的代码模板如下伪
代码所示:
synchronized(someObject) {
while(!someCondition) {
someObject.wait();
}
doSomething();
}
这里使用 while 而不是 if 的原因是,通知线程可能只是更新了保护条件中的共享
变量,但并不一定会使保护条件成立;即使通知线程可以保证保护条件成立,但
是在线程从等待集进入入口集再到获取到内部锁的这段时间内,其他线程仍然可
能更新共享变量而导致保护条件不成立。线程虽然因为保护条件不成立而进入
wait 方法,但 wait 方法的返回并不能说明保护条件已经成立。因此,在 wait 方
法返回后需要再次进行判断,若保护条件成立则执行接下来的操作,否则应该继
续进入 wait 方法。正是基于这种考虑,我们应该将 wait 方法的调用放在 while
循环而不是 if 判断中。
2.notify/notifyAll
下图是 notify 的实现机制:
和 wait 方法一样,notify 方法在执行时也必须持有对象的内部锁,否则会抛出
IllegalMonitorStateException 异常,因此 notify 方法也必须放在由该对象引导的
synchronized 同步块中。notify 方法会将等待集中的任意一个线程移出队列。和
wait 方法不同的是,notify 方法本身不会释放内部锁,而是在临界区代码执行完
成后自动释放。因此,为了使等待线程在其被唤醒之后能够尽快获得内部锁,应
该尽可能地将 notify 调用放在靠近临界区结束的地方。
调用 notify 方法所唤醒的线程是相应对象上的一个任意等待线程,但是这个被唤
醒的线程可能不是我们真正想要唤醒的那个线程。因此,有时候我们需要借助
notifyAll,它和 notify 方法的唯一不同之处在于它可以唤醒相应对象上的所有等
待线程。
3.过早唤醒问题
假设通知线程 N 和等待线程 W1 和 W2 同步在对象 obj 上,W1 和 W2 的保护条
件 C1 和 C2 均依赖于 obj 的实例变量 state,但 C1 和 C2 判断的内容并不相同。
初始状态下 C1 和 C2 均不成立。某一时刻,当线程 N 更新了共享变量 state 使
得保护条件 C1 得以成立,此时为了唤醒 W1 而执行了 obj.notifyAll()方法(调用
obj.notify()并不一定会唤醒 W1)。由于 notifyAll 唤醒的是 obj 上的所有等待线
程,因此 W2 也会被唤醒,即使 W2 的保护条件并未成立。这就使得 W2 在被唤
醒之后仍然需要继续等待。这种等待线程在保护条件并未成立的情况下被唤醒的
现象被称为过早唤醒。过早唤醒使得那些无需被唤醒的等待线程也被唤醒了,造
成了资源的浪费。过早唤醒问题可以利用下一节中介绍的 Condition 接口来解决。
二.条件变量 Condition
总的来说,Object.wait()/notify()过于底层,且 Object.wait(long timeout)还存在
过早唤醒和无法区分其返回是由于等待超时还是被通知线程唤醒的问题。不过,
了解 wait/notify 有助于我们阅读部分源码,以及学习和使用 Condition 接口。
Condition 接口可以作为 wait/notify 的替代品来实现等待/通知,它为解决过早唤
醒问题提供了支持,并解决了 Object.wait(long timeout)无法区分其返回是由于
等待超时还是被通知线程唤醒的问题。Condition 接口中定义了以下方法:
在上一篇文章中,我们在介绍 Lock 接口时曾经提到过它的 newCondition 方法,
它返回的就是一个 Condition 实例。类似于 Object.wait()/notify()要求其执行线程
必须持有这些方法所属对象的内部锁,Condition.await()/signal()也要求其执行线
程持有创建该 Condition 实例的显式锁。每个 Condition 实例内部都维护了一个
用于存储等待线程的队列。设 condition1 和 condition2 是从一个显式锁上获取
的两个不同的 Condition 实例,一个线程执行 condition1.await()会导致其被暂停
并进入 condition1 的等待队列。condition1.signal()会使 condition1 的等待队列
中的一个任意线程被唤醒,而 condition1.signaAll()则会使 condition1 的等待队
列中的所有线程被唤醒,而 condition2 的等待队列中的线程则不受影响。
和 wait/notify 类似,await/signal 的使用方法如下:
public class ConditionUsage {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void waitMethod() throws InterruptedException {
lock.lock();
try {
while (保护条件不成立) {
condition.await();
}
剩余38页未读,继续阅读
资源评论
小兔子平安
- 粉丝: 209
- 资源: 1940
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功