Reputation: 1226
I have just finished my networked job queue and I have a little question relating to the performance of a PriortiyQueue in Java.
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()
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()
?
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);
}
}
}
Upvotes: 0
Views: 2485
Reputation: 100279
In standard heap-based PriorityQueue
implementation in OpenJDK and OracleJDK the peek()
call is extremely fast:
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.
In my library I have an algorithm to select n
least elements from the unsorted input. I implemented it using the PriorityQueue
which preserves at most n
least elements found so far. First implementation was like add/poll
. When I updated to use peek
, the performance was drastically improved (up to 10x on some tests).
Upvotes: 2