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

本文介绍了Java中阻塞队列的基本概念与实现原理,详细分析了ArrayBlockingQueue的源码,包括添加、删除和查找元素等核心操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  1. public interface Queue<E> extends Collection<E> {  
  2.     // 向队列中添加元素  
  3.     boolean add(E e);  
  4.     boolean offer(E e);  
  5.   
  6.     // 删除队列元素  
  7.     E remove();  
  8.     E poll();  
  9.   
  10.     // 检查队列元素  
  11.     E element();  
  12.     E peek();  
  13. }  
public interface Queue<E> extends Collection<E> {
	// 向队列中添加元素
	boolean add(E e);
	boolean offer(E e);

	// 删除队列元素
	E remove();
	E poll();

	// 检查队列元素
	E element();
	E peek();
}
BlockingQueue类继承了如上的接口,定义如下:

  1. public interface BlockingQueue<E> extends Queue<E> {  
  2.     boolean add(E e);  
  3.     boolean offer(E e);  
  4.     void put(E e) throws InterruptedException;  
  5.     boolean offer(E e, long timeout, TimeUnit unit)  throws InterruptedException;  
  6.       
  7.     E take() throws InterruptedException;  
  8.     E poll(long timeout, TimeUnit unit)  throws InterruptedException;  
  9.       
  10.     int remainingCapacity();  
  11.     boolean remove(Object o);  
  12.       
  13.     public boolean contains(Object o);  
  14.     int drainTo(Collection<? super E> c);  
  15.     int drainTo(Collection<? super E> c, int maxElements);  
  16. }  
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种形式,如下:

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

  1. public class BlockingQueue {  
  2.     private List queue = new LinkedList();  
  3.     private int limit = 10;  
  4.   
  5.     public BlockingQueue(int limit) {  
  6.         this.limit = limit;  
  7.     }  
  8.   
  9.     public synchronized void enqueue(Object item) throws InterruptedException {  
  10.         while (this.queue.size() == this.limit) {  
  11.             wait();  
  12.         }  
  13.         if (this.queue.size() == 0) {  
  14.             notifyAll(); // 通知所有的线程来取出,如果是加入线程则继续等待  
  15.         }  
  16.         this.queue.add(item);  
  17.     }  
  18.   
  19.     public synchronized Object dequeue() throws InterruptedException {  
  20.         while (this.queue.size() == 0) {  
  21.             wait();  
  22.         }  
  23.         if (this.queue.size() == this.limit) {  
  24.             notifyAll(); // 通知所有的线程来加入,如果是取出线程则继续等待  
  25.         }  
  26.         return this.queue.remove(0);  
  27.     }  
  28. }  
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类的最主要的一个构造函数,如下:

  1. public ArrayBlockingQueue(int capacity, boolean fair) {  
  2.     if (capacity <= 0)  
  3.         throw new IllegalArgumentException();  
  4.     this.items = new Object[capacity];  
  5.     lock = new ReentrantLock(fair);  
  6.     notEmpty = lock.newCondition();  
  7.     notFull =  lock.newCondition();  
  8. }  
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、添加元素


  1. public boolean add(E e) {  
  2.     return super.add(e);  
  3. }  
  4. public boolean offer(E e) {  
  5.     checkNotNull(e);  
  6.     final ReentrantLock lock = this.lock;  
  7.     lock.lock();  
  8.     try {  
  9.         if (count == items.length)  
  10.             return false;  
  11.         else {  
  12.             insert(e);  
  13.             return true;  
  14.         }  
  15.     } finally {  
  16.         lock.unlock();  
  17.     }  
  18. }  
  19. public void put(E e) throws InterruptedException {  
  20.     checkNotNull(e);  
  21.     final ReentrantLock lock = this.lock;  
  22.     lock.lockInterruptibly();  
  23.     try {  
  24.         while (count == items.length)  
  25.             notFull.await();  
  26.         insert(e);  
  27.     } finally {  
  28.         lock.unlock();  
  29.     }  
  30. }  
  31. public boolean offer(E e, long timeout, TimeUnit unit)  throws InterruptedException {  
  32.   
  33.     checkNotNull(e);  
  34.     long nanos = unit.toNanos(timeout);  
  35.     final ReentrantLock lock = this.lock;  
  36.     lock.lockInterruptibly();  
  37.     try {  
  38.         while (count == items.length) {  
  39.             if (nanos <= 0)  
  40.                 return false;  
  41.             nanos = notFull.awaitNanos(nanos);  
  42.         }  
  43.         insert(e);  
  44.         return true;  
  45.     } finally {  
  46.         lock.unlock();  
  47.     }  
  48. }  
    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类中的方法,代码如下:

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

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

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

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

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

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

  1. final int inc(int i) {  
  2.     return (++i == items.length) ? 0 : i;  
  3. }  
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、删除元素


  1. public boolean remove(Object o) {  
  2.     if (o == nullreturn false;  
  3.     final Object[] items = this.items;  
  4.     final ReentrantLock lock = this.lock;  
  5.     lock.lock();  
  6.     try {  
  7.         for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {  
  8.             if (o.equals(items[i])) {  
  9.                 removeAt(i);  
  10.                 return true;  
  11.             }  
  12.         }  
  13.         return false;  
  14.     } finally {  
  15.         lock.unlock();  
  16.     }  
  17. }  
  18. public E poll() {  
  19.     final ReentrantLock lock = this.lock;  
  20.     lock.lock();  
  21.     try {  
  22.         return (count == 0) ? null : extract();  
  23.     } finally {  
  24.         lock.unlock();  
  25.     }  
  26. }  
  27.   
  28. public E take() throws InterruptedException {  
  29.     final ReentrantLock lock = this.lock;  
  30.     lock.lockInterruptibly();  
  31.     try {  
  32.         while (count == 0)  
  33.             notEmpty.await();  
  34.         return extract();  
  35.     } finally {  
  36.         lock.unlock();  
  37.     }  
  38. }  
  39.   
  40. public E poll(long timeout, TimeUnit unit) throws InterruptedException {  
  41.     long nanos = unit.toNanos(timeout);  
  42.     final ReentrantLock lock = this.lock;  
  43.     lock.lockInterruptibly();  
  44.     try {  
  45.         while (count == 0) {  
  46.             if (nanos <= 0)  
  47.                 return null;  
  48.             nanos = notEmpty.awaitNanos(nanos);  
  49.         }  
  50.         return extract();  
  51.     } finally {  
  52.         lock.unlock();  
  53.     }  
  54. }  
    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()方法,如下:

  1. void removeAt(int i) {                 // 移除索引处理的元素  
  2.      final Object[] items = this.items;  
  3.      // if removing front item, just advance  
  4.      if (i == takeIndex) {  
  5.          items[takeIndex] = null;  
  6.          takeIndex = inc(takeIndex); // 下一个要取出元素的索引  
  7.      } else {  
  8.          // slide over all others up through putIndex.  
  9.          for (;;) {  
  10.              int nexti = inc(i);  
  11.              if (nexti != putIndex) {  
  12.                  items[i] = items[nexti];  
  13.                  i = nexti;  
  14.              } else {  
  15.                  items[i] = null;  
  16.                  putIndex = i;  
  17.                  break;  
  18.              }  
  19.          }  
  20.      }  
  21.      --count;  
  22.      notFull.signal();             // 通知生产线程  
  23.  }  
   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索引处的值后删除这个元素,源代码如下:

  1. private E extract() {  
  2.     final Object[] items = this.items;  
  3.     // 强制将元素转换为泛型E  
  4.     E x = this.<E>cast(items[takeIndex]);  
  5.     // 将第takeIndex元素设为null,即删除。同时,帮助GC回收  
  6.     items[takeIndex] = null;  
  7.     // 设置下一个被取出元素的索引  
  8.     takeIndex = inc(takeIndex);  
  9.     --count;  
  10.     notFull.signal();  
  11.     return x;  
  12. }  
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、查找元素

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

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

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


4、遍历元素

  1. public Iterator<E> iterator() {  
  2.     return new Itr();  
  3. }  
public Iterator<E> iterator() {
    return new Itr();
}
  1. private class Itr implements Iterator<E> {  
  2.     // 队列中剩余元素的个数  
  3.     private int remaining; // Number of elements yet to be returned  
  4.     // 下一次调用next()返回的元素的索引  
  5.     private int nextIndex; // Index of element to be returned by next  
  6.     // 下一次调用next()返回的元素  
  7.     private E nextItem;    // Element to be returned by next call to next  
  8.     // 上一次调用next()返回的元素  
  9.     private E lastItem;    // Element returned by last call to next  
  10.     // 上一次调用next()返回的元素的索引  
  11.     private int lastRet;   // Index of last element returned, or -1 if none  
  12.   
  13.     Itr() {  
  14.         final ReentrantLock lock = ArrayBlockingQueue.this.lock;  
  15.         lock.lock();  
  16.         try {  
  17.             lastRet = -1;  
  18.             if ((remaining = count) > 0)  
  19.                 nextItem = itemAt(nextIndex = takeIndex);  
  20.         } finally {  
  21.             lock.unlock();  
  22.         }  
  23.     }  
  24.   
  25.     public boolean hasNext() {  
  26.         return remaining > 0;  
  27.     }  
  28.   
  29.     public E next() {  
  30.         // 获取阻塞队列的锁  
  31.         final ReentrantLock lock = ArrayBlockingQueue.this.lock;  
  32.         lock.lock();  
  33.         try {  
  34.             // 若“剩余元素<=0”,则抛出异常。  
  35.             if (remaining <= 0)  
  36.                 throw new NoSuchElementException();  
  37.             lastRet = nextIndex;  
  38.             // 获取第nextIndex位置的元素  
  39.             E x = itemAt(nextIndex);  // check for fresher value  
  40.             if (x == null) {  
  41.                 x = nextItem;         // we are forced to report old value  
  42.                 lastItem = null;      // but ensure remove fails  
  43.             }  
  44.             else  
  45.                 lastItem = x;  
  46.             while (--remaining > 0 && // skip over nulls  
  47.                    (nextItem = itemAt(nextIndex = inc(nextIndex))) == null)  
  48.                 ;  
  49.             return x;  
  50.         } finally {  
  51.             lock.unlock();  
  52.         }  
  53.     }  
  54.   
  55.     public void remove() {  
  56.         final ReentrantLock lock = ArrayBlockingQueue.this.lock;  
  57.         lock.lock();  
  58.         try {  
  59.             int i = lastRet;  
  60.             if (i == -1)  
  61.                 throw new IllegalStateException();  
  62.             lastRet = -1;  
  63.             E x = lastItem;  
  64.             lastItem = null;  
  65.             // only remove if item still at index  
  66.             if (x != null && x == items[i]) {  
  67.                 boolean removingHead = (i == takeIndex);  
  68.                 removeAt(i);  
  69.                 if (!removingHead)  
  70.                     nextIndex = dec(nextIndex);  
  71.             }  
  72.         } finally {  
  73.             lock.unlock();  
  74.         }  
  75.     }  
  76. }  
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();
        }
    }
}




转载自http://blog.youkuaiyun.com/mazhimazh/


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值