title: Thread pool that binds tasks for a given ID to the same thread
body: <p>Are there any implementations of a thread pool (in Java) that ensures all tasks for the same logical ID are executed on the same thread?</p>
  
  <p>The logic I'm after is if there is already a task being executed on a specific thread for a given logical ID, then new tasks with the same ID are scheduled on the same thread.  If there are no threads executing a task for the same ID then any thread can be used.</p>
  
  <p>This would allow tasks for unrelated IDs to be executed in parallel, but tasks for the same ID to be executed in serial and in the order submitted.</p>
  
  <p>If not, are there any suggestions on how I might extend <code>ThreadPoolExecutor</code> to get this behaviour (if that's even possible)?</p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>Having spent longer thinking about this, I don't actually require that tasks for the same logical ID get executed on the same thread, just that they don't get executed at the same time.</p>
  
  <p>An example for this would be a system that processed orders for customers, where it was OK to process multiple orders at the same time, but not for the same customer (and all orders for the same customer had to be processed in order).</p>
  
  <p>The approach I'm taking at the moment is to use a standard ThreadPoolExecutor, with a customised <code>BlockingQueue</code> and also wrapping the <code>Runnable</code> with a custom wrapper.  The <code>Runnable</code> wrapper logic is:</p>
  
  <ol>
  <li>Atomically attempt to add ID to concurrent 'running' set (<code>ConcurrentHashMap</code>) to see if a task for the same ID is currently running
  <ul>
  <li>if add fails, push the task back on to the front of the queue and return immediately</li>
  <li>if succeeeds, carry on</li>
  </ul></li>
  <li>Run the task</li>
  <li>Remove the task's associated ID from the 'running' set</li>
  </ol>
  
  <p>The queue's <code>poll()</code> methods then only return tasks that have an ID that is not currently in the 'running' set.</p>
  
  <p>The trouble with this is that I'm sure there are going to be a lot of corner cases that I haven't thought about, so it's going to require <em>a lot</em> of testing.</p>