2011-05-24 76 views
6

我使用ExecutorService爲我的應用程序編寫了一個懶惰的圖像下載程序。它使我能夠很好地控制在什麼時間並行運行多少下載等。使用LIFO訂購的執行服務

現在,我唯一的問題是,如果我提交任務,它會在隊列尾部(FIFO)結束。

有誰知道如何將此更改爲LIFO?

回答

8

您需要指定ExecutorService正在使用的隊列類型。

通常,您可能正在通過Executors中的靜態方法檢索ExecutorService。相反,您需要直接實例化並傳遞您希望提供LIFO的隊列類型。

EG,要創建LIFO線程池執行程序,可以使用以下構造函數。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) 

並作爲最終參數傳入LIFO隊列。

我知道java集合中沒有LIFO隊列(請糾正我,如果錯誤的話),但是您可以輕鬆創建一個匿名內部類來擴展LinkedBlockingQueue並覆蓋相應的方法。

例如,(未測試的)

ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 16, 1, TimeUnit.MINUTES, new LinkedBlockingQueue() { 

    @Override 
    public void put(Object obj) { 
    // override to put objects at the front of the list 
    super.addFirst(obj); 
    } 

}); 

UPDATE響應於評論。

我們可以使用封裝優先隊列的阻塞隊列。我們必須打包,因爲Executor期望可運行,但我們也需要時間戳。

// the class that will wrap the runnables 
static class Pair { 

    long timestamp; 
    Runnable runnable; 

    Pair(Runnable r) { 
     this.timestamp = System.currentTimeMillis(); 
     this.runnable = r; 
    } 
} 


    ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 16, 1, TimeUnit.MINUTES, new BlockingQueue<Runnable>() { 

     private Comparator   comparator  = new Comparator<Pair>() { 

               @Override 
               public int compare(Pair arg0, Pair arg1) { 
                Long t1 = arg0.timestamp; 
                Long t2 = arg1.timestamp; 
                // compare in reverse to get oldest first. Could also do 
                // -t1.compareTo(t2); 
                return t2.compareTo(t1); 
               } 
              }; 

     private PriorityBlockingQueue<Pair> backingQueue = new PriorityBlockingQueue<Pair>(11, comparator); 

     @Override 
     public boolean add(Runnable r) { 
      return backingQueue.add(new Pair(r)); 
     } 

     @Override 
     public boolean offer(Runnable r) { 
      return backingQueue.offer(new Pair(r)); 
     } 

     @Override 
     public boolean offer(Runnable r, long timeout, TimeUnit unit) { 
      return backingQueue.offer(new Pair(r), timeout, unit); 
     } 

     // implement/delegate rest of methods to the backing queue 
    }); 
+0

感謝您的快速響應! Unfourtunaltly LinkedBlockingQueue不附帶addFirst()方法。它可能適用於LinkedBlockingDeque,但在API lvl 9之前不可用,幾乎沒有人安裝薑餅:/無論如何。有用的答案+1。 – pumpkee 2011-05-24 09:43:39

+0

LinkedBlockingDeque源代碼可在這裏找到:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/LinkedBlockingDeque.java – greg7gkb 2012-07-19 23:47:34

2

ThreadPoolExecutor有一個constructor它允許指定要使用的隊列類型。您可以在其中插入任何BlockingQueue,並且可能有一個priority隊列可能非常適合您。您可以將優先級隊列配置爲基於添加到您下載作業的(創建)時間戳進行排序,並且執行程序將按照所需順序執行作業。

+0

按時間戳排序會很棒。將挖掘到,並嘗試使其工作:) – pumpkee 2011-05-24 09:46:05

8

你可以做到這一點在兩個或三個簡單的步驟:

  1. 創建一個LifoBlockingDeque類:

    public class LifoBlockingDeque <E> extends LinkedBlockingDeque<E> { 
    
    @Override 
    public boolean offer(E e) { 
        // Override to put objects at the front of the list 
        return super.offerFirst(e); 
    } 
    
    @Override 
    public boolean offer(E e,long timeout, TimeUnit unit) throws InterruptedException { 
        // Override to put objects at the front of the list 
        return super.offerFirst(e,timeout, unit); 
    } 
    
    
    @Override 
    public boolean add(E e) { 
        // Override to put objects at the front of the list 
        return super.offerFirst(e); 
    } 
    
    @Override 
    public void put(E e) throws InterruptedException { 
        //Override to put objects at the front of the list 
        super.putFirst(e); 
        } 
    } 
    
  2. 創建執行人:

    mThreadPool = new ThreadPoolExecutor(THREAD_POOL_SIZE, 
                THREAD_POOL_SIZE, 0L, 
                TimeUnit.MILLISECONDS, 
                new LifoBlockingDeque<>()); 
    
  3. LinkedBlockingDeque僅受API級別9支持。要在早期版本上使用,請執行以下操作:

    使用Java 1.6實現 - 從here下載它。

    然後改變

    implements BlockingDeque<E> 
    

    implements BlockingQueue<E> 
    

    爲了讓編譯Android上。 BlockingDequeBlockingQueue的子類型,所以沒有造成任何傷害。

你完成了!

+0

太棒了!只需將其修復爲'mThreadPool = new ThreadPoolExecutor(THREAD_POOL_SIZE,THREAD_POOL_SIZE,0L,TimeUnit.MILLISECONDS,new LifoBlockingDeque ());'擺脫警告 – shem 2013-04-23 09:44:08

1

我有相同的要求:延遲加載和LIFO以獲得更好的用戶體驗。所以我使用了ThreadPoolExecutor和一個封裝的BlockingQueue(如前所述)。

爲了方便向後兼容,我決定採取簡單的方法,對於較老的設備,我只是使用固定線程池 - 這意味着FIFO排序。這並不完美,但第一次嘗試沒問題。這看起來像:

 try { 
      sWorkQueue = new BlockingLifoQueue<Runnable>(); 
      sExecutor = (ThreadPoolExecutor) Class.forName("java.util.concurrent.ThreadPoolExecutor").getConstructor(int.class, int.class, long.class, TimeUnit.class, BlockingQueue.class).newInstance(3, DEFAULT_POOL_SIZE, 10, TimeUnit.MINUTES, sWorkQueue); 

      if (BuildConfig.DEBUG) Log.d(LOG_TAG, "Thread pool with LIFO working queue created"); 
     } catch (Exception e) { 
      if (BuildConfig.DEBUG) Log.d(LOG_TAG, "LIFO working queues are not available. Using default fixed thread pool"); 

      sExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_POOL_SIZE); 
     }