0

I have the use-case, where a queue holds UploadItem(s). The queue must be thread-safe. UploadItem has a method canUpload(). UploadItems are added to the queue at the tail, but peek() and poll() must return the first UploadItem where canUpload() is true. This may be the item at the head or a newer item. Essentially, I want items to be ignored until they are ready for upload, and I want the queue to return the oldest item that's ready.

To solve the job, I have extended a ConcurrentLinkedQueue and I have overridden peek() and poll(). I have synchronized the overriden methods and I am using an iterator to retrieve the first valid item.

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;
    }

}

Is this a good implementation or can this be done more efficient? Does using the iterator to retrieve items have any adverse effects? I can't pick items using an index and get() so it seems I must use the iterator. I can't track items in an internal variable either, because they are linked and changed by outside functions.

This will be run on Android and Android still uses Java 6. In face of Lock-Free Concurrent Linked List in Java and ConcurrentLinkedQueue$Node remains in heap after remove(), is there any risk that some Android phones may still have the Java memory leak? Does anybody know if Java is typically updated/patched on Android (within its major version)?

Community
  • 1
  • 1
Oliver Hausler
  • 4,900
  • 4
  • 35
  • 70

2 Answers2

1

Is this a good implementation or can this be done more efficient?

Those two criteria are not mutually exclusive.

And furthermore, without knowing how costly canUpload is, how large the queue will get, and how long the entries remain in the "not uploadable" state, it is not possible to say whether efficiency is likely to be an issue.

However, it is certainly possible to do this more efficiently. For example, if you can arrange that the transition from "not uploadable" to "uploadable" triggers an event of some kind, then you could arrange that the event handler adds the UploadItem to the queue.

And it may be that a round-robin scan of a collection of UploadItem objects will be more efficient than scanning from the start of the queue each time. Other possible strategies may work better too.

But these alternative approaches may also change the order in which uploads occur ... if that is an issue for you.

Does using the iterator to retrieve items have any adverse effects?

The iterator for a ConcurrentLinkedQueue is weakly consistent. This means that you are not guaranteed to see entries that were added after an iteration commences. That probably isn't a problem for your use-case. (I don't imagine it matters a lot if downloads occasionally get deferred to the next cycle of queue polling.)

This will be run on Android and Android still uses Java 6. In face of Lock-Free Concurrent Linked List in Java and ConcurrentLinkedQueue$Node remains in heap after remove(), is there any risk that some Android phones may still have the Java memory leak?

Those questions don't indicate a long-term memory leak to me. If there is a leak, it looks like it is likely to be cleared when the thread discards the iterator ... or something like that.

On the other hand, those Q&A's were written about Oracle / OpenJDK Java, not the Apache Harvest / Android clean-room reimplementation.

Does anybody know if Java is typically updated/patched on Android (within its major version)?

You can't generalize.

First, the Android implementation of the Java libraries are a different codebase to the standard Oracle / OpenJDK codebase. So updates / patches to the latter are not applicable to the former.

Second, even if updates / patches are made to the standard Android distros, there is no guarantee that 1) the phone manufacturers will pick them up, 2) they will hit the phone company distribution channels, or 3) individual smartphone owners will apply them.

Community
  • 1
  • 1
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • "Those two criteria are not mutually exclusive." agree ;-) canUpload() is cheap. It compares a single date variable. The queue will typically be under 100 items, but may grow if no network connection is available. I can see why you suggest a round-robin approach as performance may significantly degrade due to iterations for each poll() when the network becomes available after prolonged time and the queue is long. Thank you for discussing this. – Oliver Hausler Dec 07 '14 at 15:36
  • Based on what you said, I think that efficiency won't be a major concern. If it is, then my first suggestion (adding to the queue when the item becomes "uploadable") should be close to optimal. – Stephen C Dec 07 '14 at 22:18
0

Use a PriorityQueue with a Comparator that compares first on canUpload and then on reverse order of date.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • You are allowed to change the elements in a priority queue so that their order in the queue changes without causing undefined behavior for the queue? I can't imagine that being true (purely from an implementation point of view even) – Voo Dec 07 '14 at 08:40
  • PriorityQueue is not thread-safe, but I might be able to use PriorityBlockingQueue. – Oliver Hausler Dec 07 '14 at 15:24