《Java并发编程实战》第十四章 构建自己的同步工具定义 札记

时间:2023-03-09 16:25:59
《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、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