序
Monitor类是作为ReentrantLock的一个替代,代码中使用 Monitor比使用ReentrantLock更不易出错,可读性也更强,并且也没有显著的性能损失,使用Monitor甚至有潜在的性能得到优化。下面我们整体上对Monitor的源码结构做一下梳理,总的来说也就在从jdk最原生的wait、notify.再做了一层warp。提供更加丰富的API。
原生实现
现在假设我们要对一个变量进行线程安全的读写操作,原生jdk代码实现如下:
public class Semaphore {
private String value;
public synchronized String get() throws InterruptedException {
while (null == this.value){
wait();
}
String result = new String(this.value);
this.value = null;
notifyAll();
return result;
}
public synchronized void set(String value) throws InterruptedException {
while (null != this.value){
wait();
}
this.value = value;
notifyAll();
}
}
ReentrantLock实现
现在问题来了,如果同时有多个读线程和写线程,原生的方式无法支持更加精细的并发控制。比如当写线程写入后,只希望唤醒读线程,而原生的notifyAll方法会尝试唤醒所有等待的线程,必然引发所有读线程和写线程之间的竞争。并且代码本身表达的含义也不够友好。如果用Condition就能很好的解决这个问题。代码如下:
public class MultiCondition {
private String value;
private final ReentrantLock lock = new ReentrantLock();
private final Condition valuePresent = lock.newCondition();//read condition
private final Condition valueAbsent = lock.newCondition();//write condition
public String get() throws InterruptedException {
lock.lock();
try {
while (value == null) {
//读线程等待
valuePresent.await();
}
String result = value;
value = null;
//value置为null的时候,指定唤醒write condition.
valueAbsent.signal();
return result;
} finally {
lock.unlock();
}
}
public void set(String newValue) throws InterruptedException {
lock.lock();
try {
while (value != null) {
//value还存在,不可以写,写线程等待
valueAbsent.await();
}
value = newValue;
//指定唤醒read线程,表示可读
valuePresent.signal();
} finally {
lock.unlock();
}
}
}
Monitor实现
这样一来代码就很明确的可以看出读写线程之间的并发调度,但是这里的condition其实只是通过利用类似于多个信号量的方式来表示的,但是condition本身是没有什么实际的条件意义的。接下来看利用monitor的写法:
public class MonitorSample {
private String value;
private Monitor monitor = new Monitor();
private Monitor.Guard put = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return null == value;
}
};
private Monitor.Guard get = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return null != value;
}
};
public void set(String value) throws InterruptedException {
monitor.enterWhen(put);
this.value = value;
monitor.leave();
}
public String get() throws InterruptedException {
monitor.enterWhen(get);
String result = new String(value);
value = null;
monitor.leave();
return result;
}
}
这种方式和try lock的方式相近。可以看出来,比condition更加直观,每一个guard都可以设置一个门槛来放行,当任何一个guard达到了条件,就会被唤醒。另外moniter还提供了更多的API。
更多API
- enter():进入到当前Monitor,无限期阻塞。
- enterInterruptibly():进入到当前Monitor,无限期阻塞,但可能会被打断。
- enter(long time, TimeUnit unit):进入到当前Monitor,最多阻塞给定的时间,返回是否进入Monitor。
- enterInterruptibly(long time, TimeUnit unit):进入到当前Monitor,最多阻塞给定的时间,但可能会被打断,返回是否进入Monitor。
tryEnter():如果可以的话立即进入Monitor,不阻塞,返回是否进入Monitor。 - enterWhen(Guard guard):当Guard的isSatisfied()为true时,进入当前Monitor,无限期阻塞,但可能会被打断。
- enterWhenUninterruptibly(Guard guard):当Guard的isSatisfied()为true时,进入当前Monitor,无限期阻塞。
- enterWhen(Guard guard, long time, TimeUnit unit):当Guard的isSatisfied()为true时,进入当前Monitor,最多阻塞给定的时间,这个时间包括获取锁的时间和等待Guard satisfied的时间,但可能会被打断。
- enterWhenUninterruptibly(Guard guard, long time, TimeUnit unit):当Guard的isSatisfied()为true时,进入当前Monitor,最多阻塞给定的时间,这个时间包括获取锁的时间和等待Guard satisfied的时间。
- enterIf(Guard guard):如果Guard的isSatisfied()为true,进入当前Monitor,无限期的获得锁,不需要等待Guard satisfied。
- enterIfInterruptibly(Guard guard):如果Guard的isSatisfied()为true,进入当前Monitor,无限期的获得锁,不需要等待Guard satisfied,但可能会被打断。
- enterIf(Guard guard, long time, TimeUnit unit):如果Guard的isSatisfied()为true,进入当前Monitor,在给定的时间内持有锁,不需要等待Guard satisfied。
- enterIfInterruptibly(Guard guard, long time, TimeUnit unit):如果Guard的isSatisfied()为true,进入当前Monitor,在给定的时间内持有锁,不需要等待Guard satisfied,但可能会被打断。
- tryEnterIf(Guard guard):如果Guard的isSatisfied()为true并且可以的话立即进入Monitor,不等待获取锁,也不等待Guard satisfied。
- waitFor(Guard guard):等待Guard satisfied,无限期等待,但可能会被打断,当一个线程当前占有Monitor时,该方法才可能被调用。
- waitForUninterruptibly(Guard guard):等待Guard satisfied,无限期等待,当一个线程当前占有Monitor时,该方法才可能被调用。
- waitFor(Guard guard, long time, TimeUnit unit):等待Guard satisfied,在给定的时间内等待,但可能会被打断,当一个线程当前占有Monitor时,该方法才可能被调用。
- waitForUninterruptibly(Guard guard, long time, TimeUnit unit):等待Guard satisfied,在给定的时间内等待,当一个线程当前占有Monitor时,该方法才可能被调用。
- leave():离开当前Monitor,当一个线程当前占有Monitor时,该方法才可能被调用。
- isFair():判断当前Monitor是否使用一个公平的排序策略。
- isOccupied():返回当前Monitor是否被任何线程占有,此方法适用于检测系统状态,不适用于同步控制。
- isOccupiedByCurrentThread():返回当前线程是否占有当前Monitor。
- getOccupiedDepth():返回当前线程进入Monitor的次数,如果房前线程不占有Monitor,返回0。
- getQueueLength():返回一个估计的等待进入Monitor的线程数量,只是一个估算值,因为线程的数量在这个方法访问那不数据结构的时候可能会动态改变。此方法适用于检测系统状态,不适用于同步控制。
- getWaitQueueLength(Guard guard):返回一个等待给定Guard satisfied的线程估计数量, 注意,因为超时和中断可能发生在任何时候,所以估计只作为一个等待线程的实际数目的上限。此方法适用于检测系统状态,不适用于同步控制。
- hasQueuedThreads():返回是否有任何线程正在等待进入这个Monitor,注意,因为取消随时可能发生,所以返回true并不保证任何其他线程会进入这个Monitor。此方法设计用来检测系统状态。
- hasQueuedThread(Thread thread):返回给定线程是否正在等待进入这个Monitor,注意,因为取消随时可能发生,所以返回true并不保证给定线程会进入这个Monitor。此方法设计用来检测系统状态。
- hasWaiters(Guard guard):返回是否有任何线程正在等待给定Guard satisfied,注意,因为取消随时可能发生,所以返回true并不保证未来Guard变成satisfied时唤醒任意线程。此方法设计用来检测系统状态。