在Java中如何更改ArrayBlockingQueue的长度?

时间:2014-08-11 17:55:17

标签: java android concurrency

我正在为Android创建一个媒体播放器应用。我有两个线程:一个产生音频帧,另一个消耗这些帧。

我希望我的客户能够尝试使用不同大小的ArrayBlockedQueue,从“no”缓冲(真正的1)到最多10个缓冲块。

我似乎无法在Java中找到提供与ArrayBlockedQueue类似功能的任何类,但允许我动态地使项目列表更长/更短。

问题1)有没有人知道类似于ArrayBlockedQueue的类,但允许我更改要保留的项目数量?

然后我有一个奇怪的想法:我可以捏造它吗?我可以使用新大小创建一个新的ArrayBlockedQueue,并逐步复制当前在旧ArrayBlockedQueue中的1-10个项目并将它们放入新的ArrayBlockedQueue中,然后将旧指针存储到新的ArrayBlockedQueue上吗? / p>

由于永远不会超过10个(或者我的缓冲区限制),因此将项目复制到新数组时不应花费太多时间。

问题2)这是一种接近ArrayBlockedQueue实现的“合理”方法,仍然具有灵活性吗?

问题3)有没有更好的方法来解决这个问题?

-Ken

2 个答案:

答案 0 :(得分:1)

问题:

1)没有一个允许你手动更改队列大小,但是像LinkedBlockingQueue这样的东西增长达到你为它设置的最大值。

2和3)您可以使用文档中描述的第3个构造函数执行您所描述的内容(创建新的ArrayBlockingQueue):

ArrayBlockingQueue(int capacity,boolean fair,Collection c)

创建一个具有给定(固定)容量的ArrayBlockingQueue,指定的访问策略和最初包含给定集合的元素,以集合迭代器的遍历顺序添加。

这为您提供了您正在寻找的复制结构,并允许您设置新容量。评估:

// create the first queue
Queue smallQueue = new ArrayBlockingQueue(5);

// copy small queue over to big queue
Queue bigQueue = new ArrayBlockingQueue(10, false, smallQueue);

缩小尺寸(伪代码):

Queue bigQueue = new ArrayBlockingQueue(10);
// start processing data with your producer / consumer.

// then...
Queue smallQueue = new ArrayBlockingQueue(1);
// 1) change producer to start doing puts into the smallQueue
// 2) let consumer continue consuming from the bigQueue until it is empty
// 3) change consumer to start polling from the smallQueue

来自第1步的投注将会阻止,直到您将消费者转过来。

答案 1 :(得分:1)

您可能需要创建自己的BlockingQueue实现来包装旧队列和新队列 - 从旧队列轮询直到它为空,然后将其设置为null以防止任何内存泄漏。这样您就不会丢失旧队列中的任何待处理put

MyBlockingQueue {
  private MyBlockingQueue oldQueue
  private ArrayBlockingQueue newQueue

  ArrayBlockingQueue(int newCapacity, MyBlockingQueue _oldQueue) {
    oldQueue = _oldQueue
    newQueue = new ArrayBlockingQueue(newCapacity)
    E oldVal = null
    while(newQueue.remainingCapacity() > 0 && 
         (oldVal = oldPoll) != null)
      newQueue.put(oldVal)
  }

  boolean isEmpty() {
    (oldQueue == null || oldQueue.isEmpty) && newQueue.isEmpty 
  }

  void put(E e) {
    newQueue.put(e)
  }

  E take() {
    E oldVal = oldPoll
    if(oldVal != null) oldVal else newQueue.take
  }

  E poll() {
    E oldVal = oldPoll
    if(oldVal != null) oldVal else newQueue.poll
  }

  private E oldPoll() {
    // If you have more than one consumer thread, then use a temporary variable
    // for oldQueue - otherwise it might be set to null between the null check
    // and the call to poll
    if(oldQueue == null) null
    else {
      E oldVal = oldQueue.poll
      if(oldVal != null) oldVal
      else {
        oldQueue = null
        null
      }
    }
  }
}