【问题标题】:How to access the underlying queue of a ThreadpoolExecutor in a thread safe way如何以线程安全的方式访问 ThreadpoolExecutor 的底层队列
【发布时间】:2011-05-09 10:16:37
【问题描述】:

getQueue() 方法提供了对 ThreadPoolExecutor 中底层阻塞队列的访问,但这似乎并不安全。

遍历此函数返回的队列可能会错过 ThreadPoolExecutor 对队列所做的更新。

“方法 getQueue() 允许访问工作队列以进行监视和调试。强烈建议不要将此方法用于任何其他目的。”

如果你想遍历 ThreadPoolExecutor 使用的 workQueue,你会怎么做?还是有其他方法?

这是一个延续.. Choosing a data structure for a variant of producer consumer problem

现在,我正在尝试多生产者多消费者,但我想使用一些现有的线程池,因为我不想自己管理线程池,并且我想要在 ThreadPoolExecutor 完成执行某些任务时回调能够以线程安全的方式检查“正在进行的事务”数据结构。

【问题讨论】:

    标签: java executors


    【解决方案1】:

    您可以覆盖 beforeExecute 和 afterExecute 方法,让您知道任务已经开始和完成。您可以覆盖 execute() 以了解何时添加任务。

    您遇到的问题是队列不是为查询而设计的,并且可以在您看到之前使用任务。解决此问题的一种方法是创建自己的队列实现(可能覆盖/包装 ConcurrentLinkedQueue)

    顺便说一句:队列是线程安全的,但不能保证您会看到每个条目。

    ConcurrentLinkedQueue.iterator() 记录为

    以正确的顺序返回此队列中元素的迭代器。返回的迭代器是一个“弱一致”迭代器,它永远不会抛出 ConcurrentModificationException,并保证遍历迭代器构造时存在的元素,并且可能(但不保证)反映构造之后的任何修改。

    【讨论】:

    • 我可以在那个时间点拍摄快照,例如 Collections.unmodifiable(),但我需要知道,当我拍摄快照时,队列不会改变。所以,我需要将队列锁定在某些东西上,但 ThreadPoolExecutor 不会尊重我的锁定约束。您是否建议我创建自己的 CustomBlockingqueue,并将其传递给 ThreadPoolExecutor?我不明白这有什么帮助,请详细说明。
    • 可以将 Collections.unmodifiable() 视为原子操作吗?因为,我认为只有原子操作才是线程安全的。
    • 您遇到的问题是 ConcurrentLinkedQueue 是为并发/无锁排队而设计的。 ThreadPoolExecutor 在这件事上没有发言权。你可以给它一个行为你想要的队列。
    • 您不能将Collections.unmodifiableXxxx() 与队列一起使用。它只包装底层集合而不复制它。原子操作更容易使线程安全,但是您可以通过首先锁定集合来使 Iterator 线程安全。 (假设集合尊重锁)
    • 能否请您进一步扩展“队列是线程安全的,但不能保证您会看到每个条目。”我认为线程安全意味着每个条目都可以看到。
    【解决方案2】:

    如果你想复制队列中的项目并确保队列中的内容没有被执行,你可以试试这个:

    a) 引入暂停和恢复执行的能力。见:http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html

    b) 先暂停队列,然后复制队列,再恢复队列。

    然后我有我自己的问题。我看到的问题是,当你执行你的“Runnable”时,“Runnable”没有放在队列中,而是一个 FutureTask“包装器”,我找不到任何方法来确定我正在寻找哪一个 runnables在。因此,抓取和检查队列是毫无用处的。有人知道我错过了那里吗?

    【讨论】:

    • 谢谢保罗,你回答的最后一段完全有道理,因为我意识到我需要在单独的数据结构中维护我的可运行文件。
    【解决方案3】:

    如果您在上一个问题的accepted answer 中遵循 Jon Skeet 的建议,那么您将通过锁控制对队列的访问。如果您在进行中的队列上获得了锁,那么您可以保证遍历不会遗漏其中的任何项目。

    当然,这样做的问题是,当您进行遍历时,队列上的所有其他操作(其他生产者和消费者试图访问它)都会阻塞,这可能会对性能产生非常可怕的影响。

    【讨论】:

    • 感谢您的回复。是的,我正在尝试听从他的建议,另外使用 threadExecutor 来为我处理线程池管理。我想我可以在没有 ThreadPoolExecutor 的情况下自己完成,但是我必须编写额外的代码来处理线程池,并在任务完成时进行回调,这是我试图避免的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2010-10-07
    相关资源
    最近更新 更多