一個排序的有界的併發Buffer佇列Java實現

banq發表於2015-12-30
Java中使用TreeSet作為排序佇列或Buffer,而ConcurrentSkipListSet是支援併發的佇列,如果我們需要一個能實時排序又支援併發的佇列或Buffer怎麼辦?

設想一個場景,當我們不斷加入元素到集合,等所有元素都加入完畢後,我們只需要獲得排序的前5個元素,比如我們對半年內所有帖子或文章根據點贊量獲得前五篇點贊量最多的文章,首先我們需要將半年所有文章加入一個集合,這個集合是根據點贊量排序的,新增完畢後,我們需要獲得排名前5篇文章。

這可以透過JDK提供的 NavigableSet實現:

NavigableSet<Integer> aSet = new TreeSet<>();
    aSet.add(5);
    //..add more elements
    aSet.pollFirst();//'0'th
    aSet.pollFirst();//'1'th
    aSet = aSet.descendingSet();
    aSet.pollFirst();//'n'th
    aSet.pollFirst();//'n-1'th
<p class="indent">

但是這種集合是無界的,它需要保留所有元素,其實有時這是沒有必要的,因為我們只感興趣所有元素中子集的子集,也就是說沒有必要從資料庫中獲得所有結果。

有沒有更好的資料結構能實現有界而且像以前能夠排序呢?實際是某種BoundedNavigableSet. 而且需要支援併發訪問,多個生產者可以同時將元素放入其中。

這種資料結構能夠在分散式查詢聚合實現中非常方便,特別是對分散式key-value資料庫如Cassandra, 基於多個分割槽查詢獲得的結果需要收集融合在一起。

下面是使用AtomicReferenceArray實現的無鎖演算法插入:

/*
Scan the array from given offset to 'insert' the item
at a proper sort level
*/
boolean addItem(int fromOffset, T item)
{
    for (int i = fromOffset; i < size(); i++)
    {
      // if there is no element at 'i'th position
      // set item at 'i'
      if (!buffer.compareAndSet(i, null, item))
      {
        // compare and swap using Comparator provided
        // or, if element implements Comparable
        T swapped = compareAndSwap(i, item);
        if (swapped != null)
        {
          // the item has been placed. so break. but then
          // the element currently at 'i' has been swapped. so find its new
          // position, if present
          // we could have scanned from the 'i+1'th position, but to be safe
          // just in case some other element higher up was removed and this
          // needs to go up in that case
          if (i + 1 < size()) {
            addItem(0, swapped);
          }
          return true;
        }
      }
      else {
        return true;
      }
    }
    return false;
  }
<p class="indent">


Compare 和 swap也是一個原子操作:

private T compareAndSwap(int i, T item)
  {
    boolean set = false, greater = false;
    T t = null;
    while (!set) {
      t = buffer.get(i);
      // either i-th element was replaced with this item
      // or by some other element
  // compare using Comparator/Comparable
      greater = compare(item, t) > 0;
      set = buffer.compareAndSet(i, t, greater ? item : t);
    }
    return greater ? t : null;
  }
<p class="indent">


removal操作也應該是原子的:

public boolean remove(Object o) {
    T b;
    if(o == null)
      return false;
    for (int i = 0; i < size(); i++) {
      b = buffer.get(i);
      if (o.equals(b))
      {
        if(buffer.compareAndSet(i, b, null))
        {
          //shift left elements
          for (int j = i + i; j < size(); j++) {
            //check position form start. it can be possible that another higher item has been removed in the meantime
            addItem(0, buffer.get(j));
          }
          return true;
        }
      }
    }
    return false;
  }
<p class="indent">


完整原始碼見:Github

[該貼被banq於2015-12-30 11:25修改過]

相關文章