CompletionService - submit()不会阻止以确保创建所有线程

时间:2014-02-14 23:04:56

标签: java multithreading executorservice

我将Callables提交给ExecutorCompletionService,似乎submit()方法在提交Callables时不会阻止代码。这是我的代码:

    ExecutorService executor = Executors.newFixedThreadPool(30);
    BlockingQueue<Future<Data>> completionQueue = new LinkedBlockingQueue();
    ExecutorCompletionService<Data> completionService = new ExecutorCompletionService<Data>(executor, completionQueue);

    while(receivingPackets) {
        Callable<Data> splitPacketCallable = new SplitPacket(packetString);
        completionService.submit(splitPacketCallable);
        try {
            // Allow submit to finish
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException ex) {
            System.out.println("Something went wrong with sleeping");
        }

        try {
            Future<Data> dataFuture = completionService.poll();
            if (dataFuture != null) {
                Data data = dataFuture.get();
                fileWriter.writeLine(data.toString());
            }

        } catch (InterruptedException ex) {
            System.out.println("Error from poll: " + ex.toString());
        } catch (ExecutionException ex) {
            System.out.println("Error from get: " + ex.toString());
        }
    }

    // Finish any remaining threads
    while (!completionQueue.isEmpty()) {
        try {
            Future<Data> dataFuture = completionService.take();
            Data data = dataFuture.get();
            fileWriter.writeLine(data.toString());
        } catch (InterruptedException ex) {
            System.out.println("Error from take: " + ex.toString());
        } catch (ExecutionException ex) {
            System.out.println("Error from get: " + ex.toString());
        }
    }

    fileWriter.close();
    executor.shutdown();

有几点需要注意:

Data是一个以特殊格式存储数据的类。 SplitPacket是一个实现Callable的类,它接收已到达的String并将其拆分为块以保存在Data中。 fileWriter及其方法writeLine是一个Runnable类,它将异步写入多个线程中的单个文件。

如果我在for循环中玩睡眠,我会在输出文件中开始获得不一致的结果。如果我每次提交Callable都会睡50码,一切都很完美。但是,如果我以低值(例如0-5毫秒)提交,我开始在输出中删除线程。对我而言,这意味着submit() ExecutorCompletionService方法不会阻止。但是,因为阻止提交的callable似乎很重要,我也假设我只是实现了这个错误。

就我而言,我不知道会有多少数据包进入,因此我需要能够不断向执行者添加Callables。我用for循环而不是while循环尝试了这个,这样我就可以发送给定数量的数据包并查看它们是否在另一端打印,如果我在提交后有延迟,我只能让它们通过

有没有办法解决这个问题而不会增加hack-y延迟?

1 个答案:

答案 0 :(得分:0)

如果查看ExecutorCompletionService的来源,您会看到在任务标记为已完成后,期货将被添加到completionQueue

private class QueueingFuture extends FutureTask<Void> {
    QueueingFuture(RunnableFuture<V> task) {
        super(task, null);
        this.task = task;
    }
    protected void done() { completionQueue.add(task); }
    private final Future<V> task;
}

您可能有一个空队列但仍在运行任务。 你能做的最简单的事情就是计算任务。

int count = 0;
while(receivingPackets) {
    ...
    completionService.submit(splitPacketCallable);
    ++count;
    ...
    try {
        Future<Data> dataFuture = completionService.poll();
        if (dataFuture != null) {
            --count;
            ...
        }
    ...
}

// Finish any remaining threads
while (count-- > 0) {
    ...    
}