Java 7之多线程并发容器 - ArrayBlockingQueue

来源:互联网 发布:湖州丝绸外汇数据 编辑:程序博客网 时间:2024/05/23 02:02

Java中有些多线程编程模式在很大程序上都依赖于Queue实现的线程安全性,所以非常有必要认识,首先来看一下接口定义,如下:

public interface Queue<E> extends Collection<E> {// 向队列中添加元素boolean add(E e);boolean offer(E e);// 删除队列元素E remove();E poll();// 检查队列元素E element();E peek();}
BlockingQueue类继承了如上的接口,定义如下:

public interface BlockingQueue<E> extends Queue<E> {    boolean add(E e);    boolean offer(E e);    void put(E e) throws InterruptedException;    boolean offer(E e, long timeout, TimeUnit unit)  throws InterruptedException;        E take() throws InterruptedException;    E poll(long timeout, TimeUnit unit)  throws InterruptedException;        int remainingCapacity();    boolean remove(Object o);        public boolean contains(Object o);    int drainTo(Collection<? super E> c);    int drainTo(Collection<? super E> c, int maxElements);}
这个接口中本身定义的方法,加上从Queue接口中继承的方法后,可以将BlockingQueue方法大概分为4种形式,如下:

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列。
下面来模拟一个阻塞队列的简单实现,如下:

public class BlockingQueue {private List queue = new LinkedList();private int limit = 10;public BlockingQueue(int limit) {this.limit = limit;}public synchronized void enqueue(Object item) throws InterruptedException {while (this.queue.size() == this.limit) {wait();}if (this.queue.size() == 0) {notifyAll(); // 通知所有的线程来取出,如果是加入线程则继续等待}this.queue.add(item);}public synchronized Object dequeue() throws InterruptedException {while (this.queue.size() == 0) {wait();}if (this.queue.size() == this.limit) {notifyAll(); // 通知所有的线程来加入,如果是取出线程则继续等待}return this.queue.remove(0);}}
必须注意到,在enqueue和dequeue方法内部,只有队列的大小等于上限(limit)或者下限(0)时,才调用notifyAll方法。如果队列的大小既不等于上限,也不等于下限,任何线程调用enqueue或者dequeue方法时,都不会阻塞,都能够正常的往队列中添加或者移除元素。

Java提供了BlockingQueue接口的两个基本实现:LinkedBlockingQueue和ArrayBlockingQueue。他们都是FIFO队列,二者分别与LinkedList和ArrayList类似,但比同步List拥有更好的并发性能。他们的用法之间稍有区别,如已知队列的大小而能确定合适的边界时,用ArrayBlockingQueue非常高效。

下面来看ArrayBlockingQueue类的最主要的一个构造函数,如下:

public ArrayBlockingQueue(int capacity, boolean fair) {    if (capacity <= 0)        throw new IllegalArgumentException();    this.items = new Object[capacity];    lock = new ReentrantLock(fair);    notEmpty = lock.newCondition();    notFull =  lock.newCondition();}
Lock的作用是提供独占锁机制,来保护竞争资源;而Condition是为了更加精细的对锁进行控制,它依赖于Lock,通过某个条件对多线程进行控制。
notEmpty表示锁的非空条件。当某线程想从队列中取数据时,而此时又没有数据,则该线程通过notEmpty.await()进行等待;当其它线程向队列中插入了元素之后,就调用notEmpty.signal()唤醒之前通过notEmpty.await()进入等待状态的线程。同理,notFull表示“锁的满条件”。当某线程想向队列中插入元素,而此时队列已满时,该线程等待;当其它线程从队列中取出元素之后,就唤醒该等待的线程。

1、添加元素


    public boolean add(E e) {        return super.add(e);    }    public boolean offer(E e) {        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lock();        try {            if (count == items.length)                return false;            else {                insert(e);                return true;            }        } finally {            lock.unlock();        }    }    public void put(E e) throws InterruptedException {        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == items.length)                notFull.await();            insert(e);        } finally {            lock.unlock();        }    }    public boolean offer(E e, long timeout, TimeUnit unit)  throws InterruptedException {        checkNotNull(e);        long nanos = unit.toNanos(timeout);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == items.length) {                if (nanos <= 0)                    return false;                nanos = notFull.awaitNanos(nanos);            }            insert(e);            return true;        } finally {            lock.unlock();        }    }
(1)add(E e)方法会调用AbstractQueue类中的方法,代码如下:

   public boolean add(E e) {        if (offer(e))            return true;        else            throw new IllegalStateException("Queue full");    }
还是调用offer()方法添加元素,成功返回true,失败则抛出异常,表示队列已经满了。

(2)offer(E e)方法如果队列满了,则返回false,否则调用insert()方法进行元素的插入,这个方法的源代码如下:

 private void insert(E x) {        items[putIndex] = x;        putIndex = inc(putIndex);        ++count;                 // 元素数量加1        notEmpty.signal();       // 唤醒取元素的线程 }

理解如上代码,首先要认识两个变量的含义:takeIndex和putIndex。takeIndex表示下一个被取出元素的索引,putIndex表示下一个被添加元素的索引。它们的定义如下:

int takeIndex; // 下一个被取出元素的索引int putIndex;  // 下一个被添加元素的索引

其中inc()的源代码如下:

final int inc(int i) {    return (++i == items.length) ? 0 : i;}
当i加1后如果队列已经满了,则设置下一个被添加元素的索引为0.

(3) put(E e)方法当加入元素时,如果队列已经满了,则阻塞等待;直到检测到不满时调用insert()方法进行插入。

(4)offer(E e, long timeout, TimeUnit unit)  如果在指定的时间内还无法插入队列,则返回false,表示插入失败。否则让插入队列等待一定的时间。如果插入成功,则返回true。


2、删除元素


    public boolean remove(Object o) {        if (o == null) return false;        final Object[] items = this.items;        final ReentrantLock lock = this.lock;        lock.lock();        try {            for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {                if (o.equals(items[i])) {                    removeAt(i);                    return true;                }            }            return false;        } finally {            lock.unlock();        }    }    public E poll() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return (count == 0) ? null : extract();        } finally {            lock.unlock();        }    }    public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0)                notEmpty.await();            return extract();        } finally {            lock.unlock();        }    }    public E poll(long timeout, TimeUnit unit) throws InterruptedException {        long nanos = unit.toNanos(timeout);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0) {                if (nanos <= 0)                    return null;                nanos = notEmpty.awaitNanos(nanos);            }            return extract();        } finally {            lock.unlock();        }    }
(1)remove(Object o)方法会移除元素值相同的元素。在移除过程中需要使用removeAt()方法,如下:

   void removeAt(int i) {                 // 移除索引处理的元素        final Object[] items = this.items;        // if removing front item, just advance        if (i == takeIndex) {            items[takeIndex] = null;            takeIndex = inc(takeIndex); // 下一个要取出元素的索引        } else {            // slide over all others up through putIndex.            for (;;) {                int nexti = inc(i);                if (nexti != putIndex) {                    items[i] = items[nexti];                    i = nexti;                } else {                    items[i] = null;                    putIndex = i;                    break;                }            }        }        --count;        notFull.signal();             // 通知生产线程    }


(2)使用poll()方法时调用exact()方法,取出takeIndex索引处的值后删除这个元素,源代码如下:

private E extract() {    final Object[] items = this.items;    // 强制将元素转换为泛型E    E x = this.<E>cast(items[takeIndex]);    // 将第takeIndex元素设为null,即删除。同时,帮助GC回收    items[takeIndex] = null;    // 设置下一个被取出元素的索引    takeIndex = inc(takeIndex);    --count;    notFull.signal();    return x;}

(3)take()方法调用时,如果此时队列为空,则阻塞等待;否则调用extract()方法返回元素值。

(4)poll(long timeout, TimeUnit unit) 在指定的时间内队列仍然为空则阻塞,超过指定时间返回null;队列不空直接调用extract()方法返回元素值。



3、查找元素

public E peek() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return (count == 0) ? null : itemAt(takeIndex);        } finally {            lock.unlock();        }    }
如果队列为空,则返回null,否则调用itemAt()方法获取元素,如下:

 final E itemAt(int i) {        return this.<E>cast(items[i]); }
这个类还继承了AbstractQueue中的一个element()方法,如下:

  public E element() {        E x = peek();        if (x != null)            return x;        else            throw new NoSuchElementException();    }
调用peek()方法查找,如果元素存在,则返回,否则抛出异常。


4、遍历元素

public Iterator<E> iterator() {    return new Itr();}
private class Itr implements Iterator<E> {    // 队列中剩余元素的个数    private int remaining; // Number of elements yet to be returned    // 下一次调用next()返回的元素的索引    private int nextIndex; // Index of element to be returned by next    // 下一次调用next()返回的元素    private E nextItem;    // Element to be returned by next call to next    // 上一次调用next()返回的元素    private E lastItem;    // Element returned by last call to next    // 上一次调用next()返回的元素的索引    private int lastRet;   // Index of last element returned, or -1 if none    Itr() {        final ReentrantLock lock = ArrayBlockingQueue.this.lock;        lock.lock();        try {            lastRet = -1;            if ((remaining = count) > 0)                nextItem = itemAt(nextIndex = takeIndex);        } finally {            lock.unlock();        }    }    public boolean hasNext() {        return remaining > 0;    }    public E next() {        // 获取阻塞队列的锁        final ReentrantLock lock = ArrayBlockingQueue.this.lock;        lock.lock();        try {            // 若“剩余元素<=0”,则抛出异常。            if (remaining <= 0)                throw new NoSuchElementException();            lastRet = nextIndex;            // 获取第nextIndex位置的元素            E x = itemAt(nextIndex);  // check for fresher value            if (x == null) {                x = nextItem;         // we are forced to report old value                lastItem = null;      // but ensure remove fails            }            else                lastItem = x;            while (--remaining > 0 && // skip over nulls                   (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)                ;            return x;        } finally {            lock.unlock();        }    }    public void remove() {        final ReentrantLock lock = ArrayBlockingQueue.this.lock;        lock.lock();        try {            int i = lastRet;            if (i == -1)                throw new IllegalStateException();            lastRet = -1;            E x = lastItem;            lastItem = null;            // only remove if item still at index            if (x != null && x == items[i]) {                boolean removingHead = (i == takeIndex);                removeAt(i);                if (!removingHead)                    nextIndex = dec(nextIndex);            }        } finally {            lock.unlock();        }    }}




































0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 儿童7岁还坐不住怎么办 孩子缺锌手蜕皮裂开怎么办 宝宝读幼儿园哭怎么办 幼儿上课爱讲话怎么办 孩子上课总是乱跑怎么办 孩子听不懂老师讲课怎么办 高一上课听不懂怎么办 上课学生纪律差怎么办 一年级学生认字少怎么办 孩子上课做不住怎么办 幼儿园小孩上课乱跑怎么办 孩子挑食不爱吃饭怎么办 幼儿园孩子不听指令怎么办 1岁宝宝多动症怎么办 3岁宝宝胆小怎么办 爱挑食的孩子怎么办 小孩上课经常发呆怎么办 小孩不爱吃饭挑食怎么办 一年级小孩学习不好怎么办 宝宝上课坐不住怎么办 八个月婴儿拉稀怎么办 八个月孕妇拉稀怎么办 孩子好动爱喊怎么办 八个月小孩发烧怎么办 孩子好动怎么办学龄前教育 小孩好动症该怎么办 小孩子好动症该怎么办 怀孕5个月胎死亡怎么办 小孩多动调皮怎么办 手心老是出汗是怎么办 孩子吃饭特别慢怎么办 小孩子老想睡觉怎么办 孩子下午上课犯困怎么办 小孩子有多动症该怎么办 初中写作业犯困怎么办 孩子晚上学习困怎么办 小孩子容易兴奋激动怎么办 中考时过度兴奋怎么办 小孩兴奋不睡觉怎么办 孩子突然反常不听话怎么办? 婴儿亢奋不睡觉怎么办