title: How to optimize this simulation in Java?
body: <p>I am trying to do a multi threading simulation in Java and I have managed to do it with a queue but the execution time is high, any ideas on how I could optimize this? Can using recursion save time?</p>
  <p>The input has to be like this:</p>
  <ul>
  <li>2 5 It means that there are two threads(workers) for 5 jobs</li>
  <li>1 2 3 4 5 This is the jobs that are an integer which means the time cost of processing that job so the output will be this:</li>
  <li>0 0 The two threads try to simultaneously take jobs from the list, so thread with index 0 actually</li>
  <li>1 0 takes the first job and starts working on it at the moment 0</li>
  <li>0 1 After 1 second, thread 0 is done with the first job and takes the third job from the list, and starts processing it immediately at time 1.</li>
  <li>1 2 One second later, thread 1 is done with the second job and takes the fourth job from the list, and starts processing it immediately at time 2</li>
  <li>0 4 Finally, after 2 more seconds, thread 0 is done with the third job and takes the fifth job from the list, and starts processing it immediately at time 4</li>
  </ul>
  <p>This is the code:</p>
  <pre><code>import java.io.*;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.PriorityQueue;
  import java.util.Set;
  import java.util.StringTokenizer;
  
  public class JobQueue {
      private int numWorkers;
      private int[] jobs;
      private int[] assignedWorker;
      private long[] startTime;
  
      private FastScanner in;
      private PrintWriter out;
  
      public static void main(String[] args) throws IOException {
          new JobQueue().solve();
      }
  
      private void readData() throws IOException {
          numWorkers = in.nextInt();
          int m = in.nextInt();
          jobs = new int[m];
          for (int i = 0; i &lt; m; ++i) {
              jobs[i] = in.nextInt(); 
          }
      }
  
      private void writeResponse() {
          for (int i = 0; i &lt; jobs.length; ++i) {
              out.println(assignedWorker[i] + &quot; &quot; + startTime[i]);
          }
      }
  
      private void assignJobs() {
          // TODO: replace this code with a faster algorithm.
          assignedWorker = new int[jobs.length];
           startTime = new long[jobs.length];
           PriorityQueue&lt;Integer&gt; nextTimesQueue = new PriorityQueue&lt;Integer&gt;();
           HashMap&lt;Integer, Set&lt;Integer&gt;&gt; workersReadyAtTimeT = new HashMap&lt;Integer,Set&lt;Integer&gt;&gt;();
           long[] nextFreeTime = new long[numWorkers];
           int duration = 0;
           int bestWorker = 0;
           for (int i = 0; i &lt; jobs.length; i++) {
            duration = jobs[i];
            if(i&lt;numWorkers) {
              bestWorker = i;
              nextTimesQueue.add(duration);
              addToSet(workersReadyAtTimeT, duration, i,0);
            }else {
              int currentTime = nextTimesQueue.poll();
              Set&lt;Integer&gt; workersReady = workersReadyAtTimeT.get(currentTime);
              if (workersReady.size()&gt;1) { 
                bestWorker = workersReady.iterator().next();
                workersReady.remove(bestWorker);
                workersReadyAtTimeT.remove(currentTime);
                workersReadyAtTimeT.put(currentTime,workersReady);
                nextTimesQueue.add(currentTime);
              } else {
                bestWorker = workersReady.iterator().next();
                workersReadyAtTimeT.remove(currentTime);
                nextTimesQueue.add(currentTime+duration);
                addToSet(workersReadyAtTimeT, duration, bestWorker, currentTime);
              }
            }
            
            assignedWorker[i] = bestWorker;
            startTime[i] = nextFreeTime[bestWorker];
            nextFreeTime[bestWorker] += duration;
           }
          }
      
      private void addToSet(HashMap&lt;Integer, Set&lt;Integer&gt;&gt; workersReadyAtTimeT, int duration, int worker, int current) {
          if(workersReadyAtTimeT.get(current+duration)==null) {
            HashSet&lt;Integer&gt; s = new HashSet&lt;Integer&gt;();
            s.add(worker);
            workersReadyAtTimeT.put(current+duration, s);
          }else {
            Set&lt;Integer&gt; s = workersReadyAtTimeT.get(current+duration);
            s.add(worker);
            workersReadyAtTimeT.put(current+duration,s);
           }
          }
  
      public void solve() throws IOException {
          in = new FastScanner();
          out = new PrintWriter(new BufferedOutputStream(System.out));
          readData();
          assignJobs();
          writeResponse();
          out.close();
      }
  
      static class FastScanner {
          private BufferedReader reader;
          private StringTokenizer tokenizer;
  
          public FastScanner() {
              reader = new BufferedReader(new InputStreamReader(System.in));
              tokenizer = null;
          }
  
          public String next() throws IOException {
              while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                  tokenizer = new StringTokenizer(reader.readLine());
              }
              return tokenizer.nextToken();
          }
  
          public int nextInt() throws IOException {
              return Integer.parseInt(next());
          }
      }
  }
   
  </code></pre>