3

我正在尝试使用 Google Guava 构建缓存,并希望对过期对象进行一些计算。如果某个对象被删除,removingListener 会通知我。

如何在与主应用程序不同的线程中运行removingListener,或将过期对象(在下面的简单示例中,即整数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();
4

5 回答 5

9

要在执行程序中运行您的侦听器,请将其包装为RemovalListeners.asynchronous

.removalListener(异步(new RemovalListener() { ... }, executor))

于 2012-07-27T14:37:00.893 回答
7

使用Executors工厂方法之一创建一个 ExecutorService ,并在每次需要时向该 executor 提交一个新的 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);
}
于 2012-07-27T14:06:04.563 回答
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通过调用构造函数创建一个 new 时,一旦构造函数完成,执行就会返回到调用代码,并启动一个单独的线程来运行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();
于 2012-07-27T14:03:50.280 回答
1

您可以简单地为过期实体创建中间队列(过期监听器只会将过期对象添加到此队列中) - 比如说某种阻塞的内存队列 - ArrayBlockingQueue,LinkedBlockingDeque。

然后,您可以设置线程池和处理程序(具有可配置的大小),它们将使用 poll()方法消耗对象。

对于高性能队列 - 如果需要,我可以建议更高级的非阻塞队列实现。您还可以在此处阅读有关高性能非阻塞队列的更多信息,将第一个元素原子地添加到 ConcurrentLinkedQueue

于 2012-07-27T14:04:20.227 回答
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 的关联线程池中的线程。

于 2012-07-27T14:42:21.787 回答