Java 7之多线程第9篇 - 显式的Condition对象

来源:互联网 发布:python strip函数内容 编辑:程序博客网 时间:2024/05/17 04:33

下面这个例子使用synchronized关键字和wait() 、notifyAll()方法实现同步。

public abstract class BaseBoundedBuffer<V> {private final V[] buf;private int tail;private int head;private int count;protected BaseBoundedBuffer(int capacity) {this.buf = (V[]) new Object[capacity];}protected synchronized final void doPut(V v) {buf[tail] = v;if (++tail == buf.length)tail = 0;++count;}protected synchronized final V doTake() {V v = buf[head];buf[head] = null;if (++head == buf.length)head = 0;--count;return v;}public synchronized final boolean isFull() {return count == buf.length;}public synchronized final boolean isEmpty() {return count == 0;}}


public class BoundedBuffer <V> extends BaseBoundedBuffer<V> {    // CONDITION PREDICATE: not-full (!isFull())    // CONDITION PREDICATE: not-empty (!isEmpty())    public BoundedBuffer() {        this(100);    }    public BoundedBuffer(int size) {        super(size);    }    // BLOCKS-UNTIL: not-full    public synchronized void put(V v) throws InterruptedException {        while (isFull())            wait();        doPut(v);        notifyAll();    }    // BLOCKS-UNTIL: not-empty    public synchronized V take() throws InterruptedException {        while (isEmpty())            wait();        V v = doTake();        notifyAll();        return v;    }    // BLOCKS-UNTIL: not-full    // Alternate form of put() using conditional notification    public synchronized void alternatePut(V v) throws InterruptedException {        while (isFull())            wait();        boolean wasEmpty = isEmpty();        doPut(v);        if (wasEmpty)            notifyAll();    }}
下面来使用Condition来实现。

Condition接口中定义的方法如下所示:

public interface Condition {// 造成当前线程在接到信号或被中断之前一直处于等待状态。void await();// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。boolean await(long time, TimeUnit unit);// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。long awaitNanos(long nanosTimeout);// 造成当前线程在接到信号之前一直处于等待状态。void awaitUninterruptibly();// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。boolean awaitUntil(Date deadline);void signal();    // 唤醒一个等待线程void signalAll(); // 唤醒所有等待线程}
在Condition对象中,与wait()、notifyAll()和notify()方法相对应的分别为await、signalAll和signal。

举个具体的例子,如下:

class BoundedBuffer {    final Lock lock = new ReentrantLock();    final Condition notFull  = lock.newCondition(); // 不满    final Condition notEmpty = lock.newCondition(); // 不空    final Object[] items = new Object[5];    int putptr, takeptr, count;    public void put(Object x) throws InterruptedException {        lock.lock();               // 获取锁        try {            // 如果缓冲已满,则等待;直到缓冲不是满的,才将x添加到缓冲中            while (count == items.length)                   notFull.await();            items[putptr] = x;     // 将x添加到缓冲中            // 将put统计数putptr+1;如果缓冲已满,则设putptr为0。            if (++putptr == items.length) putptr = 0;            ++count;               // 将缓冲数量+1            notEmpty.signal();     // 唤醒take线程,因为take线程通过notEmpty.await()等待            // 打印写入的数据            System.out.println(Thread.currentThread().getName() + " put  "+ (Integer)x);        } finally {            lock.unlock();         // 释放锁        }    }    public Object take() throws InterruptedException {        lock.lock();           // 获取锁        try {            while (count == 0) // 如果缓冲为空,则等待;直到缓冲不为空,才将x从缓冲中取出                notEmpty.await();            Object x = items[takeptr]; // 将x从缓冲中取出            // 将take统计数takeptr+1;如果缓冲为空,则设takeptr为0。            if (++takeptr == items.length)               takeptr = 0;            --count;            // 将缓冲数量-1            notFull.signal();   // 唤醒put线程,因为put线程通过notFull.await()等待            // 打印取出的数据            System.out.println(Thread.currentThread().getName() + " take "+ (Integer)x);            return x;        } finally {            lock.unlock();      // 释放锁        }    } }

如上使用了ReentrantLock独占锁和Condition对象给出了有界缓存的实现,即使用Condition,分别为notFull和notEmpty。当缓存为空时,take将阻塞并等待notEmpty,此时put向notEmpty发送信号,可以解除任何在take中阻塞的线程。

编写程序运行如上的实例,如下:

public class ConditionTest {    private static BoundedBuffer bb = new BoundedBuffer();    public static void main(String[] args) {        // 启动10个写线程,向BoundedBuffer中不断的写数据(写入0-9);        // 启动10个读线程,从BoundedBuffer中不断的读数据。        for (int i=0; i<10; i++) {            new PutThread("p"+i, i).start();            new TakeThread("t"+i).start();        }    }    static class PutThread extends Thread {  // 往队列中放操作        private int num;        public PutThread(String name, int num) {            super(name);            this.num = num;        }        public void run() {            try {                Thread.sleep(1);    // 线程休眠1ms                bb.put(num);        // 向BoundedBuffer中写入数据            } catch (InterruptedException e) {    }        }    }    static class TakeThread extends Thread { // 从队列中取操作        public TakeThread(String name) {            super(name);        }        public void run() {            try {                Thread.sleep(10);                    // 线程休眠1ms                Integer num = (Integer)bb.take();    // 从BoundedBuffer中取出数据            } catch (InterruptedException e) {    }        }    }}
某一次的运行结果如下:

p1 put  1p4 put  4p5 put  5p0 put  0p2 put  2t0 take 1p3 put  3t1 take 4p6 put  6t2 take 5p7 put  7t3 take 0p8 put  8t4 take 2p9 put  9t5 take 3t6 take 6t7 take 7t8 take 8t9 take 9

更直观的表述为:

     1, p1线程向缓冲中写入1。    此时,缓冲区数据:   | 1 |   |   |   |   |
     2, p4线程向缓冲中写入4。    此时,缓冲区数据:   | 1 | 4 |   |   |   |
     3, p5线程向缓冲中写入5。    此时,缓冲区数据:   | 1 | 4 | 5 |   |   |
     4, p0线程向缓冲中写入0。    此时,缓冲区数据:   | 1 | 4 | 5 | 0 |   |
     5, p2线程向缓冲中写入2。    此时,缓冲区数据:   | 1 | 4 | 5 | 0 | 2 |
     此时,缓冲区容量为5;缓冲区已满!如果此时,还有“写线程”想往缓冲中写入数据,会调用put中的notFull.await()等待,直接缓冲区非满状态,才能继续运行。
     6, t0线程从缓冲中取出数据1。此时,缓冲区数据: |   | 4 | 5 | 0 | 2 |
     7, p3线程向缓冲中写入3。    此时,缓冲区数据:   | 3 | 4 | 5 | 0 | 2 |
     8, t1线程从缓冲中取出数据4。此时,缓冲区数据: | 3 |   | 5 | 0 | 2 |
     9, p6线程向缓冲中写入6。    此时,缓冲区数据:   | 3 | 6 | 5 | 0 | 2 |
     ...






















0 0
原创粉丝点击