2012-07-27 67 views
3

我試圖用谷歌Guava建立一個緩存,並且想要對已過期的對象進行一些計算。如果某個對象被刪除,removeListener會通知我。如何在不同的線程中運行監聽器或在不同的線程中執行計算

如何在不同於主應用程序的線程中運行removeListener,或將處理計算的過期對象(在下面的簡單示例中,將爲整數3)傳遞給另一個線程?由於計算時間很短,但經常發生,所以我寧願不要每次都創建一個新的線程(將會有數千個線程),而是有一個(或者兩個)計算所有對象。

簡單的例子:

Cache<String, Integer> cache = CacheBuilder.newBuilder().maximumSize(100) 
     .expireAfterAccess(100, TimeUnit.NANOSECONDS) 
     .removalListener(new RemovalListener<String, Integer>() { 
      public void onRemoval(final RemovalNotification notification) { 
       if (notification.getCause() == RemovalCause.EXPIRED) { 
        System.out.println("removed " + notification.getValue()); 
        // do calculation=> this should be in another thread 
       } 
      } 
     }) 
     .build(); 
cache.put("test1", 3); 
cache.cleanUp(); 

回答

9

要在執行器中運行偵聽器,請使用RemovalListeners.asynchronous進行包裝。

.removalListener(異步(新RemovalListener(){...},執行人))

+0

+1不錯的一個。非常優雅,屬於有問題的圖書館。 「只需加水」(或執行者服務) – maasg 2012-07-27 15:03:27

+0

+1。不知道這個。它消除了創建Runnable實例的樣板代碼。如果只有一些清除必須在單獨的線程中完成(而不僅僅是由過期引起的清除),但它不起作用。 – 2012-07-27 17:28:46

+0

:o我如何知道執行者傳遞給構造函數?我正在嘗試這樣的: CacheBuilder.newBuilder()。removalListener(new RemovalListener(){... – 2016-09-29 12:43:57

3

您可以創建一個新的類,並實現java.utils.Runnable接口,像這樣;

public class MyWorkerThread implements Runnable { 

    public MyWorkerThread(/*params*/) { 
     //set your instance variables here 
     //then start the thread 
     (new Thread(this)).start(); 
    } 

    public void run() { 
     //do useful things 
    } 
} 

當通過調用構造函數創建一個新的MyWorkerThread,執行一旦構造完畢返回到調用代碼,和一個單獨的線程開始運行的run()方法中的代碼。

如果您可能想要創建MyWorkerThread對象而不立即啓動對象,則可以從構造函數中刪除Thread.start()代碼,並稍後以類似方式手動從實例中調用該線程;

MyWorkerThread t = new MyWorkerThread(); 
//later 
(new Thread(t)).start(); 

或者,如果你想參考保持到Thread對象,所以你可以做常規之類的東西中斷加入,做它像這樣;

Thread myThread = new Thread(t); 
myThread.start(); 
//some other time 
myThread.interrupt(); 
+0

+1或將任務添加到ExecutorService – 2012-07-27 14:04:45

1

,你可以簡單地創建過期實體中間隊列(到期監聽器將只需要添加過期的對象到此隊列) - 表示某種阻止內存隊列 - ArrayBlockingQueue,它LinkedBlockingDeque。

然後,您可以使用poll()方法設置將消耗對象的線程池和處理程序(具有可配置的大小)。

對於高性能隊列 - 如果需要,我可以建議更高級的非阻塞隊列實現。你也可以閱讀更多關於高性能的無阻塞這裏Add the first element to a ConcurrentLinkedQueue atomically

+0

好主意。如果你可以建議一些非阻塞隊列,那會很有幫助。雖然我懷疑我必須做更多的研究,以便了解在非阻塞隊列中的演員實施 – Cassandra 2012-07-27 14:44:05

+0

中的實施概念 - 請在此處查看https://raw.github.com/gist/3181092/14e7d4f88da26aba7ef46092f464280a74abf715 /LockFreeQueue.java獲取詳細信息。 – 2012-07-27 14:47:14

6

隊列創建使用的Executors工廠方法之一ExecutorService的,並提交新的Runnable來此執行每次你需要:

private ExecutorService executor = Executors.newSingleThreadExecutor(); 

... 

public void onRemoval(final RemovalNotification notification) { 
    if (notification.getCause() == RemovalCause.EXPIRED) { 
     System.out.println("removed " + notification.getValue()); 
     submitCalculation(notification.getValue()); 
    } 
} 

private void submitCalculation(final Integer value) { 
    Runnable task = new Runnable() { 
     @Override 
     public void run() { 
      // call your calculation here 
     } 
    }; 
    executor.submit(task); 
} 
+0

嗨JB, 我知道你可以傳遞參數到新線程。但是如何讓一兩個人做所有的工作呢?如果我每次都創建一個,我很快就會有成千上萬個。 – Cassandra 2012-07-27 14:19:42

+1

這正是以上所做的。它使用單個後臺線程(因此名爲'newSingleThreadExecutor()')來執行所有提交的任務。不要混淆線程和Runnables。 Runnable只是一段可執行代碼。一個線程可以執行幾個這樣的代碼段。 – 2012-07-27 14:31:47

+0

啊,好的。對不起,我的角色很愚蠢。 – Cassandra 2012-07-27 14:54:58

1

使用執行人的服務,你的任務分派到不同的線程。 ExecutorService有一個內部阻塞隊列,用於安全發佈生產者和消費者線程之間的引用。工廠類別Executors可用於創建具有不同線程管理策略的不同ExecutorService

private ExecutorService cleanupExecutor = Executors.newFixedThreadPool(CLEANUP_THREADPOOL_SIZE); 
... 
public void onRemoval(final RemovalNotification notification) { 
    if (notification.getCause() == RemovalCause.EXPIRED) { 
     System.out.println("removed " + notification.getValue()); 
     doAsyncCalculation(notification.getValue()); 
    } 
} 

private void doAsyncCalculation(final Object obj) { 
    cleanupExecutor.submit(new Runnable() { 
     public void run() { 
      expensiveOperation(obj); 
     } 
    } 
} 

doAsyncCalculation您正在創建要運行的新任務,但不是新線程。執行程序服務負責將任務分派到executorService關聯線程池中的線程。

相關問題