必需的是一个服务,使用预先设定的时间跨度(例如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