【问题标题】:Enforce order in which jobs executed by ThreadPoolExecutor finish强制执行由 ThreadPoolExecutor 执行的作业完成的顺序
【发布时间】:2015-11-12 23:48:26
【问题描述】:

我有一堆要求很高的操作可以并行运行,但它们必须按特定顺序完成。通过完成,我的意思是将结果写入文件。当然,我可以在所有作业完成后重新排序结果,但我想避免这种情况(特别是因为结果可能太大而无法保存在内存中)。

我使用修改后的ThreadPoolExecutor 并行运行作业,确保一次只运行有限数量的作业以减少内存消耗。

我的想法是,每个作业都会引用前一个作业,并且在写入结果之前,它会等到前一个作业完成(如有必要)。这似乎是一个简单的同步问题,但由于我在这种事情上没有经验,我想听听意见,如果它真的按预期工作。还有关于内存管理。作业完成后,垃圾收集器应该能够将其从内存中删除。

abstract public class WaitingJob extends Job {

    /** Job to wait for before we finish this job */
    protected WaitingJob previousJob;

    /** Job status */
    protected boolean finished = false;

    public WaitingJob(WaitingJob previousJob) {
        this.previousJob = previousJob;
    }

    @Override
    public void run() {
        // Perform the actual job (which might decide to wait for the previous one)
        runWithWaiting();

        // Wake up any job that's waiting for us.
        synchronized (this) {
            finished = true;
            notifyAll();
        }

        // Release memory (manually, to break the chain).
        previousJob = null;
    }

    /** If the previous job is not finished yet, let's wait for it. */
    protected void waitForPreviousJob() throws InterruptedException {
        if (previousJob != null) {
            synchronized (previousJob) {
                while (!previousJob.finished) {
                    previousJob.wait();
                }
            }
        }
    }

    /** Perform the job with the possibility to wait on the previous one. */
    protected abstract void runWithWaiting();
}

示例作业:

class SampleJob extends WaitingJob {
    @Override
    protected void runWithWaiting() {
        try {
            Thread.sleep(1000);  // Do heavy work
            waitForPreviousJob();
            // Write down work
        } catch (InterruptedException e) {
        }
    }
}

我对此进行了几次测试,它似乎表现得应该如此,但我不确定是否有任何危险时刻。

另外,有没有现成的更好的解决方案?

【问题讨论】:

    标签: java multithreading synchronization


    【解决方案1】:

    在我看来这应该可行并且是一个合理的解决方案。唯一的潜在问题是,根据您的代码的其余部分,所有线程可能都被正在等待尚未分配线程的作业的作业占用,从而导致死锁(例如,池中有四个线程,作业 2-5 完成并将其线程置于等待状态,而作业 1 无法获得线程,因为它们都被占用了)。不过,这可能不适用于您的情况。

    【讨论】:

    • 我将作业按照处理顺序插入到执行程序中,并通过阻塞队列。根据队列的定义,我希望作业 2 不可能在作业 1 之前获得线程。还是我错了?
    • 这应该没问题,只要作业提交到队列的顺序是明确定义的,例如,如果它们都是从同一个线程提交的。如果作业是从多个线程提交的,您可能需要检查具体的阻塞队列实现和配置。
    • 是的,它定义明确。作业生成是一个相当便宜的操作,因此它由单个线程处理,该线程将作业推送到 Executor 内的 Blockqueue。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    • 1970-01-01
    • 2017-03-25
    相关资源
    最近更新 更多