簡體   English   中英

如何運行后台線程定期清理列表中的某些元素?

[英]How can I run a background thread that cleans up some elements in list regularly?

我目前正在實施緩存。 我已完成基本實現,如下所示。 我想要做的是運行一個線程,刪除滿足特定條件的條目。

class Cache {
    int timeLimit = 10; //how long each entry needs to be kept after accessed(marked)
    int maxEntries = 10; //maximum number of Entries
    HashSet<String> set = new HashSet<String>();   
    public void add(Entry t){
        ....
    }

    public Entry access(String key){
        //mark Entry that it has been used
        //Since it has been marked, background thread should remove this entry after timeLimit seconds.
        return set.get(key);
    }
    ....
}

我的問題是,我應該如何實現后台線程,以便線程繞過集合中的條目並刪除已marked && (last access time - now)>timeLimit

編輯

上面只是代碼的簡化版本,我沒有寫同步語句。

你為什么重新發明輪子? EhCache (以及任何體面的緩存實現)都會為您完成此任務。 也更輕巧 MapMaker 來自Guava的 Cache可以自動刪除舊條目。

如果你真的想自己實現它,那就不是那么簡單了。

  1. 記住同步。 您應該使用ConcurrentHashMapsynchronized關鍵字來存儲條目。 這可能真的很棘手。

  2. 你必須以某種方式存儲每個條目的最后訪問時間。 每次訪問條目時,都必須更新該時間戳。

  3. 想想驅逐政策。 如果緩存中有多個maxEntries ,首先要刪除哪些?

  4. 你真的需要一個后台線程嗎?

    這是令人驚訝的,但EhCache(企業就緒且經過驗證)不使用后台線程來使舊條目無效)。 相反,它會等到地圖已滿並懶惰地刪除條目。 這看起來是一個很好的權衡,因為線程很昂貴。

  5. 如果你有一個后台線程,那么每個緩存還是一個全局? 您是在創建新緩存時啟動新線程還是擁有所有緩存的全局列表? 這比你想象的要難......

一旦你回答了所有這些問題,實現就相當簡單了:每隔一秒左右查看所有條目,如果滿足你已編寫的條件,則刪除條目。

首先,要synchronized或使用對集合的訪問權限 ConcurrentHashSet 基於ConcurrentHashMapSet如下面的注釋所示。

其次,編寫新線程,並將其實現為無限循環,定期迭代先前的集合並刪除元素。 您應該以在構造函數中使用正確的集合初始化它的方式編寫此類,這樣您就不必擔心“如何訪問正確的集合”。

我個人使用GuavaCache類型。 它已經是線程安全的,並且內置了一些方法,可以根據時間限制從緩存中逐出。 如果你想要一個線程定期掃描它,你可以這樣做:

    new Thread(new Runnable() {
        public void run() {
            cache.cleanUp();
            try { Thread.sleep(MY_SLEEP_DURATION); } catch (Exception e) {};
        }
    }).start();

我不認為你真的需要一個后台線程。 相反,您可以在執行查找之前或之后刪除過期的條目。 這簡化了整個實現,很難區分。

順便說一句:如果使用LinkedHashMap,可以通過覆蓋removeEldestEntry將其用作LRU緩存(有關示例,請參閱其javadoc)

首先,你呈現的代碼是不完整的,因為HashSet上沒有get(key) (所以我假設你的意思是某種Map )並且你的代碼沒有提到任何“標記”。 還有很多方法可以進行緩存,如果不知道要緩存的內容以及原因,很難找出最佳解決方案。

實現緩存時,通常假設數據結構將由多個線程同時訪問。 因此,您需要做的第一件事是使用線程安全的后備數據結構。 HashMap不是線程安全的,但是ConcurrentHashMap是。 還有許多其他並發Map實現,即GuavaJavolution高級lib 除了地圖之外,還有其他方法可以構建緩存,它們的用處取決於您的用例。 無論如何,即使您決定不需要后台線程,而且在嘗試從緩存中檢索過期對象時,也很可能需要使后備數據結構成為線程安全的。 或者讓GC使用SoftReference刪除條目。

一旦你的高速緩存的內部線程安全,你可以簡單地啟動一個新的(很可能是守護進程)線程,定期掃描/迭代緩存並刪除舊條目。 線程將在循環中執行此操作(直到被中斷,如果您希望能夠再次停止它),然后在每次掃描后休眠一段時間。

但是,您應該考慮構建自己的緩存實現是否值得。 編寫線程安全代碼並不容易,我建議您在嘗試編寫自己的緩存實現之前先研究它。 我可以推薦Java Concurrency in Practice一書。

當然,更簡單的方法是使用現有的緩存實現。 Java-land中有許多選項,都有自己獨特的權衡取舍。

  • EhCacheJCS都是通用緩存,適合大多數緩存需求,可以在典型的“企業”應用程序中找到。
  • Infinispan是一種針對分布式使用進行了優化的緩存,因此可以緩存比單個機器上的數據更多的數據。 我也喜歡它的基於ConcurrentMap的API。
  • 正如其他人所提到的,Googles Guava庫有一個Cache API,對於小型內存緩存非常有用。

由於您希望限制緩存中的條目數,因此您可能對對象池而不是緩存感興趣。

  • Apache Commons-Pool被廣泛使用,其API類似於您自己構建的API。
  • 另一方面, Stormpot有一個相當不同的API,我幾乎只提到它,因為我寫了它。 它可能不是你想要的,但是誰能確定你不知道你要緩存什么以及為什么?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM