一、状态依赖性的管理
有界缓存实现的基类
@ ThreadSafe
public abstract class BaseBoundedBuffer<E> {
@GuardeBy( "this" ) private final E[] buf;
@GuardeBy( "this" ) private int tail;
@GuardeBy( "this" ) private int head;
@GuardeBy( "this" ) private int count;
protected BaseBoundedBuffer( int capacity) {
this .buf = (E[]) new Object[capacity];
}
protected synchronized final void doPut(E E) {
buf[tail] = E;
if (++tail == buf.length) {
tail = 0;
}
++count;
}
protected synchronized final E doTake() {
E E = buf[head];
buf[head] = null ;
if (++head == buf.length) {
head = 0;
}
--count;
return E;
}
public synchronized final boolean isFull() {
return count == buf.length;
}
public synchronized final boolean isEmpty() {
return count == 0;
}
}
1 示例:将前提条件的失败传递给调用者
@ ThreadSafe
public class GrumpyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
public GrumpyBoundedBuffer( int size){
super (size);
}
public synchronized void put(V v){
if (isFull()){
throw new BufferFullException ();
}
doPut(v);
}
public synchronized V take(){
if (isEmpty())
throw new BufferEmptyExeption ();
return doTake();
}
}
缓存为空或者已满都不是异常情况,使用者必须要捕获这些异常才能进行正确的处理。
while (true ){
try {
V item = buffer.take();
// 对于item执行一些操作
break ;
} catch (BufferEmptyException e) {
Thread. sleep(SLEEP_GRANULARITY );
}
}
2 示例:通过轮询与休眠来实现简单的阻塞
从上面的代码可以看出,阻塞与出现异常都需要方法的使用者来处理,现在尝试都封装到有界缓存中。
@ ThreadSafe
public class SleepyBoundedBuffer<V> extends BaseBoundedBuffer<V> {
public SleepyBoundedBuffer( int size) {
super (size);
}
public void put(V v) throws InterruptedException{
while (true ){
synchronized (this ){
if (!isFull()){
doPut(v);
return ;
}
}
Thread.sleep(SLEEP_GRANULARITY);
}
}
public V take() throws InterruptedException{
while (true ){
synchronized (this ){
if (!isEmpty()){
return doTake();
}
}
Thread.sleep(SLEEP_GRANULARITY);
}
}
}
3 条件队列
不需要使用while(true),改为使用wait、notifyAll
@ ThreadSafe
public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
// 条件谓词:not-full (!isFull())
// 条件谓词:not-empty (!isEmpty())
public BoundedBuffer( int size) {
super (size);
}
// 阻塞并直道:not-full
public synchronized void put(V v) throws InterruptedException{
while (isFull()){
wait();
}
doPut(v);
notifyAll();
}
// 阻塞并直道:not-empty
public synchronized V take() throws InterruptedException{
while (isEmpty()){
wait();
}
V v = doTake();
notifyAll();
return v;
}
}
二、使用条件队列
1 条件谓词
要想正确地使用条件队列,关键是找出对象在哪个条件谓词上等待。
2 过早唤醒
例如:内置条件队列中有多个条件谓语,此时如果调用notifyAll其含义是通知所有wait,但是并不一定所有条件谓语都满足执行条件。
当使用条件等待时(例如Object.wait或Condition.await):
. 通常都有一个条件谓词--包括一些对象状态的测试,线程在执行前必须首先通过这些测试。
. 在调用wait之前测试条件谓词,并且从wait中返回时再次进行测试。
. 在一个循环中调用wait。
. 确保使用与条件队列相关的锁来保护构成条件谓词的各个状态变量。
. 当调用wait、notify或notifyAll等方法时,一定要持有与条件队列相关的锁。
. 在检查条件谓词之后以及开始执行相应的操作之前,不要释放锁。
. 通常都有一个条件谓词--包括一些对象状态的测试,线程在执行前必须首先通过这些测试。
. 在调用wait之前测试条件谓词,并且从wait中返回时再次进行测试。
. 在一个循环中调用wait。
. 确保使用与条件队列相关的锁来保护构成条件谓词的各个状态变量。
. 当调用wait、notify或notifyAll等方法时,一定要持有与条件队列相关的锁。
. 在检查条件谓词之后以及开始执行相应的操作之前,不要释放锁。
3 丢失的信号
已经满足通知的条件发出通知,但是之后才进入阻塞wait状态,所以wait永远等不到在其前面发出的notify。
4 通知
5 示例:阀门类
6 子类的安全问题
7 封装条件队列
8 入口协议与出口协议
三、显式的Condition对象
四、Synchronizer剖析
五、AbstractQueuedSynchronizer
六、java.util.concurrent同步器类中的 AQS
1 ReentrantLock
2 Semaphore与CountDownLatch
3 FutureTask
4 ReentrantReadWriteLock