简体   繁体   English

Java PriorityQueue:轮询()然后再添加()或窥视()然后再删除()是否更好?

[英]Java PriorityQueue: Is it better to poll() and then add() or peek() and then remove()

I have just finished my networked job queue and I have a little question relating to the performance of a PriortiyQueue in Java. 我刚完成网络作业队列,但我对Java中PriortiyQueue的性能存在一些疑问。

Take this code: 采取以下代码:

 private void performJob() {
    lock.lock();
    try {
        NetworkJob job = actions.poll();
        if (job.perform()) {
            return;
        }
        actions.add(job); //Job was a failure, add it back to the queue
    } finally {
        lock.unlock();
    }
}

in the case of the job failing, the job still needs to be in the queue. 在作业失败的情况下,该作业仍需要排队。 So, my question: Is it better to poll() and then add() or peek() and then remove() 所以,我的问题是: poll()然后再add()还是peek()然后再remove()更好?

I am personally leaning towards the code below, but considering that a job shouldn't really be failing (In most cases, assume it was a pass) is it better to just poll() ? 我个人倾向于下面的代码,但是考虑到工作不应该真的失败(在大多数情况下,假设它是通过的)最好只使用poll()吗?

 private void performJob() {
    lock.lock();
    try {
        NetworkJob job = actions.peek();
        if (!job.perform()) {
            return;
        }
        actions.remove(); //Job was a success,  we can remove it from the queue.
    } finally {
        lock.unlock();
    }
}

Totally nitpicking and probably not worth worrying about due to the rarely-used nature of the queue, but it interests me and I'd like to see your reasoning. 由于队列很少使用,这完全是挑剔的,也许不值得担心,但是它让我很感兴趣,我希望看到您的推理。

Full code: 完整代码:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;

import java.util.PriorityQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public final class NetworkQueue implements Runnable {

    private final Context context;
    private final AtomicBoolean running = new AtomicBoolean(true);
    private final PriorityQueue<NetworkJob> actions = new PriorityQueue<>(5, new NetworkJobComparator());
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition jobReady = lock.newCondition();
    private final Condition networkUp = lock.newCondition();

    private ConnectionType connection = ConnectionType.NONE;

    public NetworkQueue(Context context) {
        this.context = context;
        context.registerReceiver(new NetworkListener(),
                new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
    }

    @Override
    public void run() {
        try {
            while (running.get()) {
                waitJobAvailable();
                waitNetworkUp();
                performJob();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void setNetwork(ConnectionType net) {
        lock.lock();
        try {
            connection = net;
            if (connection != ConnectionType.NONE) {
                networkUp.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    private void waitNetworkUp() throws InterruptedException {
        lock.lock();
        try {
            while (connection != ConnectionType.NONE) {
                networkUp.await();
            }
        } finally {
            lock.unlock();
        }
    }


    private void waitJobAvailable() throws InterruptedException {
        lock.lock();
        try {
            while (actions.isEmpty()) {
                jobReady.await();
            }
        } finally {
            lock.unlock();
        }
    }

    private void performJob() {
        lock.lock();
        try {
            NetworkJob job = actions.peek();
            if (!job.perform()) {
                return;
            }
            actions.remove();
        } finally {
            lock.unlock();
        }
    }

    public boolean addJob(NetworkJob job) {
        lock.lock();
        try {
            if (this.actions.contains(job)) {
                return false;
            }
            this.actions.add(job);
            this.jobReady.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    public void end() {
        this.running.set(false);
    }

    private class NetworkListener extends BroadcastReceiver {

        ConnectivityManager conn = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        @Override
        public void onReceive(Context context, Intent intent) {
            NetworkInfo networkInfo = conn.getActiveNetworkInfo();
            if (networkInfo == null) {
                setNetwork(ConnectionType.NONE);
                return;
            }
            if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                setNetwork(ConnectionType.WIFI);
                return;
            }
            setNetwork(ConnectionType.ANY);
        }
    }

}

In standard heap-based PriorityQueue implementation in OpenJDK and OracleJDK the peek() call is extremely fast: 在OpenJDK和OracleJDK中基于标准堆的PriorityQueue标准实现中, peek()调用非常快:

public E peek() {
    return (size == 0) ? null : (E) queue[0];
}

That's because heap root is always the least element. 这是因为堆根始终是最小的元素。 In contrast removing and adding operations can be quite expensive as they may need to restructure the heap. 相反,删除和添加操作可能非常昂贵,因为它们可能需要重组堆。 Thus peek/remove solution is likely to be faster. 因此, peek/remove解决方案可能会更快。

In my library I have an algorithm to select n least elements from the unsorted input. 在我的图书馆我有一个算法来选择n从无序输入最少的元素。 I implemented it using the PriorityQueue which preserves at most n least elements found so far. 我使用PriorityQueue实现了它,该n最多可以保留到目前为止找到的至少n元素。 First implementation was like add/poll . 第一个实现类似于add/poll When I updated to use peek , the performance was drastically improved (up to 10x on some tests). 当我更新为使用peek ,性能得到了极大的提高(在某些测试中达到10倍)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM