On a few server side projects I have worked on, I needed a special threading structure that keeps repeating the same task over and over (in most cases it blocks because it needs to wait for something like input/output etc). In some cases I just needed a single thread, but in most cased I needed multiple threads. I have played with the Executor to accomplish this goal:
- reposting a task as soon as it completes. The problem with this solution is that it is quite tricky if you want to increase the number of threads because no task is available for them to execute. The reposting also increases the complexity of the task, and complexity is something I try to prevent (especially in server side environments)
- a modified BlockingQueue that keeps handing out the same task instead of it being removed. This solution was given at the concurrency mailinglist, but it still doesn’t feel right: the structure is not used as it was intended, and this also complicates this already complicated subject.
That is why I decided to create a new threading structure: the Repeater. The Repeater is a structure that is able to keep repeating the same task over and over again. The default implementation of the Repeater is the ThreadPoolRepeater: as the name states, it has a pool of threads that keep repeating the same task.
How it works
The workerthreads in the repeater try to get the current task. If no task is available, they block until one comes available (or until they are interrupted). I have moved this ‘blocking until reference comes available’ behavior in a new structure: the LendableReference. If a task is placed in the LendableReference, the workerthreads wakeup, execute the task, and finally take the task back (I’ll get back to this) and wait for a new one to come available. If the same task is still in the LendeableReference, the workerthreads can repeat the same task over and over again.
How it can be used
I love concurrency control, but I prefer to keep the number of objects that are aware of concurrency as small as possible. In most cases this is possible to extract all threading behavior from objects. With repeaters this also is quite easy to realize: I see a method as an axle and a Repeater as an engine. I can hook up the engine to the axle from the outside (I use Spring for this task, but something else could be used as well). This approach is perfect for a production environment, because you have a very clear separation of concerns (and this makes is easy to reason about a system or to alter its behavior). For testing purposes, you can call the method yourself without worrying about multi threading.
Relaxed or Strict
The task in the Repeater can be changed. If it is changed, it could happen that at some moment more than one task is being executed by the Repeater (some threads still execute the old task and some threads are executing the new task). In some cases this is very undesirable behavior. That is why this behavior can be customized in the Repeater. In the previous section I introduced the LendableReference; a structure you can lend a value from, and before obtaining a new reference, the old one needs to be returned. I have created two different implementation of this LendableReference:
- StrictLendableReference: this implementation doesn’t allow a new reference to be placed before all references are returned. This makes it impossible that different references are lend at any given moment.
- RelaxedLendableReference: this implementation does allow a new reference to be placed before all reference are returned. This makes it possible that different references are lend at any given moment. This gives the RelaxedLendableReference better concurrency characteristics because taking and putting a reference don’t block.
So by using different LendableReference implementations in the Repeater, you can control if different tasks can be run at the same moment. This behavior is quite difficult to realize with the Executor.
There are various other aspects of the Repeater you want to control in a server side environment. You want to control the threads it uses; that is why you can inject a ThreadFactory. But this is not the only thing: in my previous blogpost I told about the WaitPoints (a point, threads need to pass before they can continue). By creating a new LendableReference that acts as a decorator to a target LendableReference, and where all lends need to pass the WaitPoint, you can open en close the Repeater, and you can even throttle it (the amount of time between executions). Another thing I’m playing with is to create some sort of predicate that can remove the task from further execution.
The Repeater, the LendableReferences and the WaitPoints are part of the concurrency library I’m working on, and I hope to make a first release in 1 or 2 months time (it really is a lot of work: documentation, testing, writing code, a site, etc).