A design pattern for a multithreaded Java application

I have a backend that receives feature requests

update(by_whom, payload);

      

Then I process the request and respond accordingly.

Each time a user submits a request , the message goes through several processing objects before receiving a response.

Now, how could I handle each user in my own thread? I don't understand how to dispatch events to individual threads.

Example:

User 1

sends the first request -> We create ThreadUser1 and process it there.

User 2

sends the first request -> We create ThreadUser2 and handle it there, it doesn't matter if request1 from user1 hasn't finished, they can work together.

User 1

sends the second request, the first one it sent is not complete -> We are sending this request to threadUser1 and so it has to wait for the request to complete.

Basically, once a request is received, it should only be queued to those sent by the same user, and run concurrently with everything else.

The way I thought would work:

I thought about posting each request to a parallel list from the main thread, and the threads check that list ( while true ) and fetch the requests from the user they belong to.

Several problems with this: - I will need to check the entire list every time for the request of the desired user.

  • This assumes that I already have a thread for every possible user that I don't know, I only know the user id when he first writes.

  • Efficiency, potentially a huge number of loops checking the same list over and over.

  • The thread should stop after all requests from this user have completed.

At this last point: this is because I need to have a MAX constraint whereby any received requests when we already have MAX threads , with these new requests not owned by any current user (aka running thread) is processed fine (queues are mostly stream, one by one).

I really don't know how to do this, and I would be very glad if someone could point me in the right direction.

+3


source to share


1 answer


You need your threads to know what task they are running on and what user they are running from. However, you want to limit the number of threads.

I think architecturally the best way to do this is to run a limited number of threads and let them handle tasks from arbitrary users, but not allow multiple threads to run for the same user at the same time.

To do this, save three sets of custom objects, one set for all users with incomplete tasks, one set for users who have a thread assigned to them, and one set for users who do not have a thread assigned to them.Place all three sets into a single tracker thread safe for the user. You will also need a Map from users to tasks, or if you need to complete tasks in the order in which they are queued, from users to task queues.

When you enqueue a task, synchronize with the user tracker, then select the Set users with incomplete tasks check box. If a matching user does not exist, create it and place it in the user set with incomplete tasks and also in the set of users without an associated thread. Then put the task into a thread safe map from users to tasks. Finally, get out of sync with the user tracker. The easiest way to do this synchronization is to move this processing into a synchronized method in the user tracker.

Each user is assigned a stream, which can be zero. The thread starts its loop, synchronizing the user tracking object, then gets the job if available.



To get a task, the thread first checks to see if it has been assigned to another user. If so, it checks the card for tasks for that user. If it finds one, the thread will remove the task from the map for maintenance. If the user does not find any tasks for the user, it removes the user from the set of users with incomplete tasks and from the set of users with dedicated threads, and then sets the user to null.

If a thread is assigned to a null user, it checks a set of users with no assigned threads. If it finds one, it assigns itself to that user and moves that user from the set of users without assigned threads to the Set of users with assigned threads. The thread then checks the map for the tasks associated with that user - there is guaranteed to be one - and deletes it for service.

After receiving the task, but before serving it, the thread exits the block of code that was synchronized with the user's tracking object. Again, it is best to use a synchronized method in the user tracking object for the task receiving process. After exiting a synchronized block, the thread ends the task. If the thread ultimately didn't receive the task because none of them were available other than users who already had threads assigned, the thread might sleep for a while before waking up and rechecking, but that should be outside of the sync block.

Perhaps you can define specific classes to use yourself. You can get more sophisticated problems if the threads are closed instead of sleeping and then reload them as needed, but it's best to simplify the process first.

-1


source







All Articles