[TOC]
4.1 模式简介
- 我正坐在餐馆中,合计着吃点什么。想好之后,我举起手示意服务员点菜。于是,看到我举手的服务员就向我走来点菜。这时,另一位服务员也看到我举手示意了,但他看到已经有一位服务员走向了我,所以就没有再过来。
- 如果现在不适合执行这个操作,或者没必要执行这个操作,就停止处理,直接返回——这就是Balking模式。
- 所谓Balk,就是 “停止并返回” 的意思。
- Balking 模式与Guarded Suspension模式一样,也存在守护条件。在Balking模式中,如果守护条件不成立,则立即中断处理。这与Guarded Suspension模式有所不同,因为Guarded Suspension模式是一直等待至可以运行。
4.3 Balking模式中的角色
4.3.1 GuardedObject
- GuardedObject角色是一个拥有被防护的方法(guardedMethod)的类。当线程执行guardedMethod方法时,若守护条件成立,则执行实际的处理。而当守护条件不成立时,则不执行实际的处理,直接返回。守护条件的成立与否,会随着GuardedObject角色的状态变化而发生变化。
- 除了guardedMethod之外,GuardedObject角色还有可能有其他来改变状态的方法(stateChangingMethod)。
4.3.2 类图和Timethreads图
4.4 拓展思路的要点
4.4.1 何时使用
- 并不需要执行时。
- 在这里执行balk能够提高程序性能。
- 不需要等待守护条件成立时。
- Balking模式的特点就是“不进行等待”。若守护条件不成立时,想要立即返回并进入下一个操作,就可以使用Balking模式。这能够提高程序的响应性。
- 守护条件仅在第一次成立时。
- 具体实例经常是实例的初始化和终止处理等。例如下列代码中的Something类。在这里,initialized字段表示初始化是否已经完成。当init方法被调用后,它会首先来检查initialized字段。
- 如果initialized字段的值为true,就表明字段已经初始化了,此时需要使用return执行balk(也可以设计成抛出异常)。
- 如果initialized字段的值为false,就需要调用doInit方法执行实际的初始化处理,然后将initialized字段设置为true,来记录“初始化已经完成了”这一事实(这相当于没有stateChangingMethod方法的情况)。
public class P448_Reorder.Something
{
private boolean initialized = false;
public synchronized void init()
{
if (initialized) // 此处不能使用while,因为initialized字段一旦为true就不会再变为false
{
return;
}
doInit();
initialized = true;
}
private void doInit()
{
// 实际的初始化处理
}
}
4.4.2 balk结果的表示方式
- 忽略balk。
- 通过返回值来表示balk。
- 通过异常来表示balk的发生。
4.6 超时
4.6.1 Timeout
- 在Balking模式中,当守护条件不成立时,线程会直接balk并返回。
- 在Guarded Suspension模式中,当守护条件不成立时,线程会一直等待到成立为止。
- 介于 “直接balk并返回” 和 “等待到守护条件成立为止” 这两种极端的处理方法之间,还有一种处理方法,那就是 “在守护条件成立之前等待一段时间”。在守护条件成立之前等待一段时间,如果到时条件还未成立,则直接balk。我们将这种处理称为guarded timed 或timeout。
4.6.2 wait何时终止
- notify方法执行时。
- notifyAll方法执行时。
- interrupt方法执行时。
- 超时发生时。
其中notify和notifyAll这两个方法是用于调用实例的,而interrupt方法是用于调用线程的。当被interrupt时,等待队列中的线程(与被notify、notifyAll时一样)会重新获取obj的锁,然后抛出InterruptedException异常。
4.6.3 synchronized和wait状态的区别
对于线程的下列两种状态:
- 想要使用synchronized获取锁但处于阻塞状态
- 执行wait并进入等待队列的状态
在这两种状态下,线程都是不运行的,这一点两者非常相似,但也存在不同:
- 我们无法让状态1下的线程发生超时。这是因为,synchronized方法和synchronized代码块都无法设置超时时间。
- 另外,即使对状态1下的线程执行interrupt,也不会抛出InterruptedException异常。线程必须在获取锁并进入synchronized之后,调用那些会注意着中断状态的方法,如wait、sleep、join等,或者使用isInterrupted方法或interrupted方法检查中断状态,自己抛出去。