Java中基于时间的聚合器

时间:2016-04-25 10:23:41

标签: java multithreading scheduled-tasks future concurrent.futures

问题

必需的是一个服务,使用预先设定的时间跨度(例如3秒)进行初始化,随着时间的推移接收工作(对象),每次3秒都没有新的工作,它将所有累积的工作作为一个集合。

每次发布​​项目集合时,该集合应由用户指定的处理器处理(请参阅下面的ElementsProcessor)。

示例

假设预设时间跨度为3000毫秒,收到的工作为:

1 -- 1000ms -- 2 -- 1000ms -- 3 -- 1000ms -- 4 -- 3500ms -- 5

结果应该是项目4到达后3秒发布的项目1,2,3,4,项目5将在稍后发布,一次3秒,没有新工作通过。

我的临时解决方案

我尝试使用ScheduledFuture实现此功能,但遇到了一个问题,因为它似乎可以告诉它任务是否已完成,但无论是否已启动。与FutureTask相同的问题。我所拥有的一个限制是,一旦开始工作,就不应该被打断。

所以我不得不求助于一些丑陋的黑客看着ScheduledFuture的延迟。所以我相信我可能会做错事,我很乐意听到更好的想法。

作为参考,我把我的代码(2个文件)放在这里:

import java.util.Collection;

/**
 * @param <E>   element type.
 * @param <V>   the result type of method {@link #process(Collection)}.
 */
public interface ElementsProcessor<E, V>
{
    public V process(Collection<E> elements);
}

import static java.util.concurrent.TimeUnit.MILLISECONDS;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Deque;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;

public class WorkAggregator<E, V>
{
    private final ElementsProcessor<E, V> elementsProcessor;
    private final long waitPeriod;
    private Deque<E> elements = new ArrayDeque<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private ScheduledFuture<V> scheduledFuture;

    public WorkAggregator(ElementsProcessor<E, V> elementsProcessor, long waitPeriod)
    {
        this.elementsProcessor = elementsProcessor;
        this.waitPeriod = waitPeriod;
    }

    public synchronized void add(E element)
    {
        attemptToCancelScheduledWork();
        elements.add(element);
        print("In add(), added element " + element + ", elements=" + elements);
        Handler<E, V> handler = new Handler<E, V>(elementsProcessor, elements);
        scheduledFuture = scheduler.schedule(handler, waitPeriod, MILLISECONDS);
    }

    private void attemptToCancelScheduledWork()
    {
        if (scheduledFuture == null)
            return;
        long delay = scheduledFuture.getDelay(MILLISECONDS);
        print("In attemptToCancelScheduledWork(), scheduledFuture delay=" + delay);
        if (delay < 50 || !scheduledFuture.cancel(false)) // delay < 50 - ugly hack!
            elements = new ArrayDeque<>(); // if we weren't able to cancel work we need to remove it from future processing
    }

    public synchronized void flush()
    {
        attemptToCancelScheduledWork();
        if (!elements.isEmpty())
            elementsProcessor.process(elements);
    }

    public synchronized void close() throws InterruptedException
    {
        print("In close() elements=" + elements);
        flush();
        print("In close() scheduler=" + scheduler);
        scheduler.shutdown();
        scheduler.awaitTermination(1000, MILLISECONDS);
    }

    private static void print(String s)
    {
        System.out.format("%tT  %-16s %s\n", Calendar.getInstance(), Thread.currentThread().getName(), s);
    }

    public static void main(String... args) throws InterruptedException
    {
        long[] sleepTimes = { 1000, 1000, 1000, 3500, 3500, 1000, 3500, 1000, 1000 }; // each value is the sleep AFTER the element is added
        ElementsProcessor<String, Boolean> elementsProcessor = new ElementsProcessor<String, Boolean>()
        {
            @Override
            public Boolean process(Collection<String> elements)
            {
                print("Starting processing " + elements);
                try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
                print("Finished processing " + elements);
                return null;
            }
        };
        WorkAggregator<String, Boolean> workAggregator = new WorkAggregator<>(elementsProcessor, 3000);
        print("Start, sleep-times=" + Arrays.toString(sleepTimes));
        for (int i = 1; i <= sleepTimes.length; i++)
        {
            workAggregator.add(String.valueOf(i));
            try { Thread.sleep(sleepTimes[i - 1]); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        workAggregator.close();
        print("The end");
    }
}

class Handler<E, V> implements Callable<V>
{
    private final ElementsProcessor<E, V> elementsProcessor;
    private final Deque<E> elements;

    Handler(ElementsProcessor<E, V> elementsProcessor, Deque<E> elements)
    {
        this.elementsProcessor = elementsProcessor;
        this.elements = elements;
    }

    @Override
    public V call() throws Exception
    {
        return elementsProcessor.process(elements);
    }
}

main()的输出是:

19:58:01  main             Start, sleep-times=[1000, 1000, 1000, 3500, 3500, 1000, 3500, 1000, 1000]
19:58:01  main             In add(), added element 1, elements=[1]
19:58:02  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:02  main             In add(), added element 2, elements=[1, 2]
19:58:03  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:03  main             In add(), added element 3, elements=[1, 2, 3]
19:58:04  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:04  main             In add(), added element 4, elements=[1, 2, 3, 4]
19:58:07  pool-1-thread-1  Starting processing [1, 2, 3, 4]
19:58:08  main             In attemptToCancelScheduledWork(), scheduledFuture delay=-500
19:58:08  main             In add(), added element 5, elements=[5]
19:58:08  pool-1-thread-1  Finished processing [1, 2, 3, 4]
19:58:11  pool-1-thread-1  Starting processing [5]
19:58:11  main             In attemptToCancelScheduledWork(), scheduledFuture delay=-500
19:58:11  main             In add(), added element 6, elements=[6]
19:58:12  pool-1-thread-1  Finished processing [5]
19:58:12  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:12  main             In add(), added element 7, elements=[6, 7]
19:58:15  pool-1-thread-1  Starting processing [6, 7]
19:58:16  main             In attemptToCancelScheduledWork(), scheduledFuture delay=-500
19:58:16  main             In add(), added element 8, elements=[8]
19:58:16  pool-1-thread-1  Finished processing [6, 7]
19:58:17  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:17  main             In add(), added element 9, elements=[8, 9]
19:58:18  main             In close() elements=[8, 9]
19:58:18  main             In attemptToCancelScheduledWork(), scheduledFuture delay=1999
19:58:18  main             Starting processing [8, 9]
19:58:19  main             Finished processing [8, 9]
19:58:19  main             In close() scheduler=java.util.concurrent.ScheduledThreadPoolExecutor@3d4eac69[Running, pool size = 1, active threads = 0, queued tasks = 1, completed tasks = 8]
19:58:19  main             The end

0 个答案:

没有答案