流以固定速率生成

时间:2015-05-05 12:11:15

标签: java java-8

我正在使用Stream.generate从Instagram获取数据。由于Instagram限制每小时的呼叫,我希望generate每2秒运行频率更低。

我之所以选择这样的头衔,是因为我从ScheduledExecutorService.scheduleAtFixedRate移开了,这就是我要搜索的内容。我确实意识到流中间操作是懒惰的,不能按计划调用。如果您对标题有更好的了解,请告诉我。

所以我希望在种族之间至少有2秒的延迟。

我的尝试没有考虑generate之后的操作消耗的时间,这可能需要2秒以上的时间:

Stream.generate(() -> {
            List<MediaFeedData> feedDataList = null;
            while (feedDataList == null) {
                try {
                    Thread.sleep(2000);
                    feedDataList = newData();
                } catch (InstagramException e) {
                    notifyError(e.getMessage());
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            return feedDataList;
        })

3 个答案:

答案 0 :(得分:4)

解决方案是将发生器与Stream分离,例如使用BlockingQueue

final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(100);

ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
scheduler.scheduleAtFixedRate(() -> {
    // Generate new data every 2s, regardless of their processing rate
    ThreadLocalRandom random = ThreadLocalRandom.current();
    queue.offer(random.nextInt(10));
}, 0, 2, TimeUnit.SECONDS);

Stream.generate(() -> {
    try {
        // Accept new data if ready, or wait for some more to be generated
        return queue.take();
    } catch (InterruptedException e) {}
    return -1;
}).forEach(System.out::println);

如果数据处理时间超过2秒,则新数据将入队并等待消耗。如果它少于2秒,则生成器中的take方法将等待调度程序生成新数据。

通过这种方式,您可以确保每小时向Instagram拨打少于N个电话!

答案 1 :(得分:2)

据我了解,你的问题是解决两个问题:

  • 以固定费率而非固定延迟等待
  • 为未知数量的项目创建流,允许处理直到某个时间点(即不是无限的)

您可以使用基于截止日期的等待来解决第一项任务,第二项任务可以通过实施Spliterator来解决:

Stream<List<MediaFeedData>> stream = StreamSupport.stream(
    new Spliterators.AbstractSpliterator<List<MediaFeedData>>(Long.MAX_VALUE, 0) {
        long lastTime=System.currentTimeMillis();
        @Override
        public boolean tryAdvance(Consumer<? super List<MediaFeedData>> action) {
            if(quitCondition()) return false;
            List<MediaFeedData> feedDataList = null;
            while (feedDataList == null) {
                lastTime+=TimeUnit.SECONDS.toMillis(2);
                while(System.currentTimeMillis()<lastTime)
                    LockSupport.parkUntil(lastTime);
                try {
                    feedDataList=newData();
                } catch (InstagramException e) {
                    notifyError(e.getMessage());
                    if(QUIT_ON_EXCEPTION) return false;
                }
            }
            action.accept(feedDataList);
            return true;
        }
    }, false);

答案 2 :(得分:1)

制作计时器和信号量。计时器每隔2秒就会提升一次信号量,并在流中等待每次调用信号量。

这使等待时间保持在指定的最小值(2秒),而且 - 有趣的是 - 甚至可以使用.parallel()

private final volatile Semaphore tickingSemaphore= new Semaphore(1, true);

在自己的主题中:

Stream.generate(() -> {
     tickingSemaphore.acquire();
     ...
};

在计时器中:

tickingSemaphore.release();
相关问题