Java并发编程实战

来源:互联网 发布:主机域名是什么 编辑:程序博客网 时间:2024/05/22 03:14

如何创建线程安全的状态依赖的类?
状态依赖操作如一直阻塞直到某个条件为真。

四种实现方法:
方法一:在类库中现有状态依赖类的基础上进行构造
如利用BlockingQueue, FutureTask, CountdownLatch, Semaphore 等构建状态依赖的类。这是首选的简单安全的方法。

示例:下面的例子通过CountDownLatch只设值一次,如果没有设值就等待。

public class ValueLatch <T> {    private T value = null;    private final CountDownLatch done = new CountDownLatch(1);    public boolean isSet() {        return (done.getCount() == 0);    }    public synchronized void setValue(T newValue) {        if (!isSet()) {            value = newValue;            done.countDown();        }    }    public T getValue() throws InterruptedException {        done.await();        synchronized (this) {            return value;        }    }}

方法二:内置的条件队列
条件队列:一种等待线程集合能够通过某种方式等待特定条件为真。
每个Java对象既可以作为锁,也可以作为条件队列。
Object的wait, notify, notifyAll 方法构造内部条件队列的API。

条件等待中的三元关系: 锁,wait方法和条件谓词。
条件谓词就是使某个操作称为状态依赖操作的前提条件。如缓存不为空时,take方法才能执行,否则等待。

状态依赖方法的典型模式:

void stateDependentMethod() throws InterruptedException {    //通过锁来保护条件谓词    synchronized(lock) {        while(!conditionPredicate())            lock.wait();    }        }

当使用条件等待时(Object.wait 或 Condition.await):

  • 通常都有一个条件谓词对一些对象状态的测试。
  • 在调用wait之前测试条件谓词,并且从wait中返回时再次进行测试。
  • 在一个循环中调用wait。
  • 确保使用与条件谓词相关的锁来保护构成条件谓词的各个状态变量。
  • 当调用wait, notify, notifyAll 方法时,一定要持有与条件队列相关的锁。
  • 在检查条件谓词之后以及开始执行相应的操作之前,不要释放锁。

大多数情况下使用notifyAll,只有同时符合下面两个条件时才用notify:
1. 所有等待线程的类型都相同。只有一个条件谓词与条件队列相关,并且每个线程在从wait返回后将执行相同的操作。
2. 单进单出。在条件变量的的每次通知,最多只唤醒一个线程来执行。

方法三:显示的Condition对象
内置条件队列有不足,如内置锁只能有一个相关联的条件队列。
Lock和Condition可以使用带有多个条件谓词的并发对象,或对条件队列有更多的控制权,如限时,公平性。

示例:

public class ConditionBoundedBuffer <T> {    protected final Lock lock = new ReentrantLock();    // CONDITION PREDICATE: notFull (count < items.length)    private final Condition notFull = lock.newCondition();    // CONDITION PREDICATE: notEmpty (count > 0)    private final Condition notEmpty = lock.newCondition();    private static final int BUFFER_SIZE = 100;    private final T[] items = (T[]) new Object[BUFFER_SIZE];    private int tail, head, count;    // BLOCKS-UNTIL: notFull    public void put(T x) throws InterruptedException {        lock.lock();        try {            while (count == items.length)                notFull.await();            items[tail] = x;            if (++tail == items.length)                tail = 0;            ++count;            notEmpty.signal();        } finally {            lock.unlock();        }    }    // BLOCKS-UNTIL: notEmpty    public T take() throws InterruptedException {        lock.lock();        try {            while (count == 0)                notEmpty.await();            T x = items[head];            items[head] = null;            if (++head == items.length)                head = 0;            --count;            notFull.signal();            return x;        } finally {            lock.unlock();        }    }}

方法四:AbstractQueuedSynchronizer(AQS)
AQS是一个用于构建锁和同步器的框架。
基于AQS的同步器,只可能在一个时刻发生阻塞,从而降低上下文切换的开销,提供吞吐量和可伸缩性。
在基于AQS的同步器,最基本的操作是获取操作和释放操作。

java.util.concurrent包中很多可阻塞类都是基于AQS,
如 ReentrantLock, Semaphore, CountdownLatch, ReentrantReadWriteLock, SynchronousQueue, FutureTask等。

0 0
原创粉丝点击