《Java并发编程实战》第十四章 构建自己的同步工具定义 札记
一、状态依赖性的管理
有界缓存实现的基类
@ 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中返回时再次进行測试。
. 在一个循环中调用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