A simple but not perfect solution to this problem is to maintain a set of auxiliary queues in the array equal to the number of processed threads that you use. One main thread pulls items from your main queue and adds them to an additional queue, indexed modulo the hash code of the object (the hash code of anyone identifying and associated with your tasks).
eg.
int queueIndex = myEntity.getKey().hashCode() % queues.length;
Only one chain processes this queue, and all tasks for the same object will be sent to this queue, so race conditions will not be.
This solution is imperfect, as some threads may end in large queues than others. In practice, this hardly matters, but it is something that needs to be taken into account.
Problems with a simple solution:
A simpler solution for pulling items from one queue, and then blocking on something different for the affected object, has a race condition (as Aurand pointed out). Given:
Master Queue [ Task1(entity1), Task2(entity1), ... ]
Where task1 and task2 modify the same entity1 , and thread1 and thread2 work in the queue, then the expected / desired sequence of events:
- Thread1 accepts task1
- Lock Thread1 in essence1
- Thread1 edits an entity1
- Thread1 unlocks entity1
- Thread2 accepts task2
- Thread2 blocks entity1
- Thread2 edits an entity1
- Thread2 unlocks entity1
Unfortunately, even if the lock is the first instruction in the thread start method, the following sequence is possible:
- Thread1 accepts task1
- Thread2 accepts task2
- Thread2 blocks entity1
- Thread2 edits an entity1
- Thread2 unlocks entity1
- Thread1 blocks entity1
- Thread1 edits an entity1
- Thread1 unlocks entity1
To avoid this, each thread would have to block something (say, a queue) before executing a task from the queue, and then obtain an object lock, while maintaining the parent lock. However, you donβt want to lock everything by holding this parental lock and waiting for an object to be locked, so you only need to try to lock the object and then handle the case when it cannot receive (maybe put it in another queue), In general the situation is becoming non-trivial.