【问题标题】:Is ConcurrentLinkedQueue a good choice if poll does not necessarily return the first item?如果 poll 不一定返回第一项,ConcurrentLinkedQueue 是一个不错的选择吗?
【发布时间】:2014-12-07 06:28:58
【问题描述】:

我有一个用例,其中一个队列保存 UploadItem(s)。队列必须是线程安全的。 UploadItem 有一个方法 canUpload()。 UploadItems 被添加到队列的尾部,但 peek() 和 poll() 必须返回 canUpload() 为 true 的第一个 UploadItem。这可能是头部的项目或更新的项目。本质上,我希望项目在准备好上传之前被忽略,并且我希望队列返回准备好的最旧项目。

为了解决这个问题,我扩展了 ConcurrentLinkedQueue 并覆盖了 peek() 和 poll()。我已经同步了被覆盖的方法,并且正在使用迭代器来检索第一个有效项目。

public class UploadQueue extends ConcurrentLinkedQueue<UploadItem> {

    public UploadQueue() {}

    @Override public synchronized UploadItem peek() {
        for (UploadItem item : this) {
            if (item.canUpload()) {
                return item;
            }
        }
        return null;
    }

    @Override public synchronized UploadItem poll() {
        for (UploadItem item : this) {
            if (item.canUpload()) {
                remove(item);

                return item;
            }
        }
        return null;
    }

}

这是一个好的实现还是可以更有效地完成?使用迭代器检索项目是否有任何不利影响?我无法使用索引和 get() 选择项目,所以看来我必须使用迭代器。我也无法跟踪内部变量中的项目,因为它们是由外部函数链接和更改的。

这将在Android上运行并且Android仍然使用Java 6。面对Lock-Free Concurrent Linked List in JavaConcurrentLinkedQueue$Node remains in heap after remove(),是否存在某些Android手机可能仍然存在Java内存泄漏的风险?有人知道 Java 是否通常在 Android 上更新/修补(在其主要版本内)?

【问题讨论】:

    标签: java android multithreading concurrency queue


    【解决方案1】:

    这是一个好的实现还是可以更有效地完成?

    这两个标准并不相互排斥。

    此外,在不知道canUpload 的成本有多大、队列将有多大以及条目保持“不可上传”状态多长时间的情况下,无法说效率是否可能是一个问题.

    但是,当然可能更有效地做到这一点。例如,如果您可以安排从“不可上传”到“可上传”的转换触发某种事件,那么您可以安排事件处理程序将 UploadItem 添加到队列中。

    可能对UploadItem 对象的集合进行循环扫描比每次从队列开头扫描更有效。其他可能的策略也可能效果更好。

    但这些替代方法也可能会改变上传发生的顺序......如果这对您来说是个问题。

    使用迭代器检索项目是否有任何不利影响?

    ConcurrentLinkedQueue 的迭代器是weakly consistent。这意味着您不能保证看到在迭代开始后添加的条目。对于您的用例而言,这可能不是问题。 (我认为如果下载偶尔会延迟到下一个队列轮询周期,这并不重要。)

    这将在Android上运行,并且Android仍然使用Java 6。面对Lock-Free Concurrent Linked List in JavaConcurrentLinkedQueue$Node remains in heap after remove(),是否存在某些Android手机仍然存在Java内存泄漏的风险?

    这些问题对我来说并不意味着长期内存泄漏。如果有泄漏,看起来它很可能在线程丢弃迭代器时被清除......或类似的东西。

    另一方面,这些问答是关于 Oracle / OpenJDK Java,而不是 Apache Harvest / Android 无尘室重新实现。

    有人知道 Java 是否通常在 Android 上更新/修补(在其主要版本内)?

    你不能一概而论。

    首先,Java 库的 Android 实现是与标准 Oracle / OpenJDK 代码库不同的代码库。所以后者的更新/补丁不适用于前者。

    其次,即使对标准 Android 发行版进行了更新/补丁,也不能保证 1) 手机制造商会接受它们,2) 他们会打到手机公司的分销渠道,或 3) 个别智能手机用户将应用它们。

    【讨论】:

    • “这两个标准并不相互排斥。”同意 ;-) canUpload() 很便宜。它比较单个日期变量。队列通常少于 100 个项目,但如果没有可用的网络连接,则可能会增加。我可以理解为什么您建议采用循环方法,因为当网络在长时间后可用并且队列很长时,由于每次 poll() 的迭代,性能可能会显着下降。感谢您讨论这个问题。
    • 根据您所说的,我认为效率不会成为主要问题。如果是,那么我的第一个建议(当项目变为“可上传”时添加到队列中)应该接近最佳。
    【解决方案2】:

    PriorityQueueComparator 一起使用,首先比较canUpload,然后按相反的日期顺序进行比较。

    【讨论】:

    • 您是否可以更改优先级队列中的元素,以便它们在队列中的顺序发生变化而不会导致队列出现未定义的行为?我无法想象这是真的(甚至从实现的角度来看)
    • PriorityQueue 不是线程安全的,但我或许可以使用 PriorityBlockingQueue。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-10
    • 1970-01-01
    • 2015-06-29
    • 2011-07-07
    • 1970-01-01
    相关资源
    最近更新 更多