java - what is the best collection for this use case?

Viewed 49

I have a list of intensive updates so I am grouping them together and executing them as a batch job in a single thread. Other threads can send their updates at any time.

class ItemUpdateJob {
    int itemId;
    int number;
}

When scheduling a job to be queued for updating, I want a collection where I can modify a job if it already exists (assuming itemId as the key). In this example:

existingItemJobInQueue.number += requestedItemJob.number;

so the queue doesn't start having thousands of jobs for the same item. When the jobs begin execution I will need to somehow loop through the queue, but while updating a job, it should not be updated (should each item have its own lock?).

for (ItemUpdateJob job : jobQueue) {
    updateItem(job);
}

Once a job has been updated, it should immediately be removed from the queue. What is the best way to do this? Currently I am thinking of using a HashMap with the item id as the key, then each item has a lock which prevents an existing job from being modified while the item is being updated. Although, this will cause a halt as it waits for the update to complete (lock to be released).

1 Answers

It looks to me as if you need a combination of more than one collection. Perhaps something like this?

public class JobHandler {

  //jobs still in the queue, map for a quick lookup
  private final Map<Integer, ItemUpdateJob> waitingJobs;
  //jobs still waiting to be run
  private final Queue<ItemUpdateJob> jobQueue;

  public JobHandler(Collection<ItemUpdateJob> jobs) {
    this.waitingJobs = new HashMap<>();
    this.jobQueue = new LinkedList<>();
    this.init(jobs);
  }
  
  private void init(Collection<ItemUpdateJob> jobs) {
    for (ItemUpdateJob job : jobs) {
      this.waitingJobs.put(job.itemId, job);
      this.jobQueue.add(job);
    }
  }

  public ItemUpdateJob getNextJobToRun() {
    ItemUpdateJob nextJob = this.jobQueue.poll();
    if (nextJob != null) {
      this.waitingJobs.remove(nextJob.itemId);
    }
    return nextJob;
  }

  public void addJob(ItemUpdateJob job) {
    this.waitingJobs.put(job.itemId, job);
    this.jobQueue.add(job);
  }

  public boolean updateJob(ItemUpdateJob updateJob) {
    if (this.waitingJobs.containsKey(updateJob.itemId)) {
      //job is currently waiting for execution, so update it
      this.waitingJobs.get(updateJob.itemId).number += updateJob.number;
      return true;
    } else {
      //job is currently being run, or no such job at all
      //so adding it at the end of the queue to wait for it's turn
      this.addJob(updateJob);
      return false;
    }
  }
}

java.util.Queue looks like a good match - FIFO order of execution for jobs and a Map for quick lookups when updating currently waiting job. Keep in mind some Queue implementations have capacity restrictions, and obviously this needs synchronization.

Related