1 Monitor介绍
无论使用对象监视器的wait notify/notifyAll还是Condition的await signal/ signalAll方法调用,我们首先都会对共享数据的临界值进行判断,当条件满足或者不满足的时候才会调用相关方法使得当前线程挂起,或者唤醒wait队列/set中的线程,因此对共享数据临界值的判断非常关键,Guava的Monitor工具提供了一种将临界值判断抽取成Guard的处理方式,可以很方便地定义若干个Guard也就是临界值的判断,以及对临界值判断的重复使用,除此之外Monitor还具备synchronized关键字和显式锁Lock的完整语义
2 入门案例
public class MonitorExample1 {
/**
* 定义Monitor对象
*/
private static Monitor monitor = new Monitor();
/**
* 共享数据,一个简单的int类型数据
*/
private static int x = 0;
/**
* 定义临界值,共享数据的值不能超过MAX_VALUE
*/
private static final int MAX_VALUE = 10;
/**
* 定义Guard 并且实现isSatisfied方法
*/
private static final Monitor.Guard INC_WHEN_LESS_10 = new Monitor.Guard(monitor) {
/**
* 该方法就相当于我们在写对象监视器或者Condition时的临界值判断逻辑
* @return
*/
@Override
public boolean isSatisfied() {
return x < MAX_VALUE;
}
};
public static void main(String[] args) throws InterruptedException {
while (true) {
monitor.enterWhen(INC_WHEN_LESS_10);
try {
x++;
System.out.println(currentThread().getName() + ": x value is: " + x);
}finally {
monitor.leave();
}
}
}
}
- 我们首先定义了一个Monitor对象,接着又将临界值的判断抽取成了Guard,我们只需要将临界值的判断逻辑写在isSatisfied()方法中即可,当共享数据的值大于10的时候无法对其再次进行自增操作。
- 在main方法中(main线程中),我们采取无限循环的方式对共享数据x进行自增操作
- 对x进行操作之前先调用monitor.enterWhen()方法,该方法除了具备锁的功能之外还具备临界值判断的操作,因此只有当x满足临界值判断时当前线程才会对x进行自增运算,否则当前线程将会进入阻塞队列(其实在Guard内部使用的也是Condition)
- 对x的运算成功之后,调用leave()方法,该方法除了释放当前的锁之外,还会通知唤醒与Guard关联的Condition阻塞队列中的某个阻塞线程。
运行上面的代码,我们会发现临界值条件不满足时,当前线程(main线程)将会进入阻塞状态:
main: x value is: 1
main: x value is: 2
main: x value is: 3
main: x value is: 4
main: x value is: 5
main: x value is: 6
main: x value is: 7
main: x value is: 8
main: x value is: 9
main: x value is: 10
当某个线程进入Monitor代码块时,实际上它首先要抢占与Monitor关联的Lock,当该线程调用了leave方法,实际上是需要释放与Monitor关联的Lock,因此在某个时刻仅有一个线程能够进入到Monitor代码块中(排他的)
3 Monitor的其他方法
enter():该方法完全等价于Lock的lock()方法。
enterIf(Guard guard):该方法主要用于判断当前的Guard是否满足临界值的判断,也是使用比较多的一个操作,调用该方法,当前线程并不会进入阻塞之中。
tryEnter():等价于Lock的tryLock()方法。
waitFor(Guard guard):当前线程将会阻塞等待,直到Guard的条件满足当前线程才会退出阻塞状态。