java中的ScheduledThreadPoolExecutor用於定時任務, 它使用最小堆來存放定時任務, Timer也是採用最小堆, 它們之間的區別:java
因此, 儘可能用ScheduledThreadPoolExecutor來代替Timer算法
本文主要講ScheduledThreadPoolExecutor中的DelayedWorkQueue的最小堆實現。先看最小堆的含義: 一種徹底二叉樹, 父結點的值小於或等於它的左子節點和右子節點. 以下圖 數組
DelayedWorkQueue使用數組來存儲定時任務, 按照從上到下, 從左到右的順序依次排列: 多線程
第K個節點的左子節點位於2K+1的位置, 右子節點位於2K+2的位置, 父結點位於(K-1)/2的位置this
依然使用上圖的例子, 如今要插入數據**0
**
第1步, 插入末尾的位置 線程
第2步, 0比19要小, 須要向上移動 code
第3步, 0比2要小, 再上移 對象
第4步, 0比1要小, 再上移
至此結束.blog
以上面的最後一圖爲例, 假設刪除**1
** 索引
使用最後一個結點來填充被移除的位置
19與較小的子節點(2)交換位置
至此結束
N個數據的最小堆, 共有logN層, 最壞的狀況下, 須要移動logN次.
ScheduledThreadPoolExecutor的最小堆實現爲DelayedWorkQueue
public boolean offer(Runnable x) { if (x == null) throw new NullPointerException(); RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x; final ReentrantLock lock = this.lock; lock.lock(); try { int i = size; if (i >= queue.length) grow(); //數組長度不夠了, 增大一倍 size = i + 1; if (i == 0) { //空列隊, 直接放在首位置 queue[0] = e; //index用於快速定位對象在數組中的位置 setIndex(e, 0); } else { //i爲最末尾的位置, 從i開始上移 siftUp(i, e); } if (queue[0] == e) { leader = null; available.signal(); } } finally { lock.unlock(); } return true; }
private void siftUp(int k, RunnableScheduledFuture<?> key) { while (k > 0) { int parent = (k - 1) >>> 1;//計算節點k的父節點 RunnableScheduledFuture<?> e = queue[parent]; if (key.compareTo(e) >= 0) //子節點大於等於父節點, 父節點不須要再移動, 結束 break; queue[k] = e; //父節點大於子節點, 將父節點移到下面 setIndex(e, k); k = parent; } queue[k] = key; //最終找到的合適位置 setIndex(key, k); }
public boolean remove(Object x) { final ReentrantLock lock = this.lock; lock.lock(); try { int i = indexOf(x); //索引已經存放在了x的成員變量中, 直接取出 if (i < 0) //若是定時任務已經被取消了, index會被置成-1 return false; setIndex(queue[i], -1); int s = --size; RunnableScheduledFuture<?> replacement = queue[s]; //最末尾的對象 queue[s] = null; if (s != i) { //若是s == i, 則表示x是最末尾的對象, 不須要更多的操做 siftDown(i, replacement); //將最末尾的對象, 從位置i開始下移.(由siftDown來判斷須要不須要下移) if (queue[i] == replacement) //若是並無下移, 那確定就是須要上移了. siftUp(i, replacement); } return true; } finally { lock.unlock(); } }
private void siftDown(int k, RunnableScheduledFuture<?> key) { int half = size >>> 1; while (k < half) { int child = (k << 1) + 1; //左子節點位置 RunnableScheduledFuture<?> c = queue[child]; //左子節點對象 int right = child + 1; //右子節點位置 if (right < size && c.compareTo(queue[right]) > 0) //左右子節點中, 選出較小的那個子節點 c = queue[child = right]; if (key.compareTo(c) <= 0) //key比子節點都小, 不須要再移動 break; queue[k] = c; //較小的子節點上移 setIndex(c, k); k = child; } queue[k] = key; //爲key找到的最終位置 setIndex(key, k); }