HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Asynchronous task execution using actor based concurrency

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
actorasynchronoususingbasedconcurrencyexecutiontask

Problem

I have a program where I need to implement asynchronous tasks (writing a directory to a file following any change to the directory). There is existing documentation within my organization for implementing tasks, which includes actions and actors. Most of it deals with distributed activity across multiple servers, which my application does not deal with, so I've really had to strip out much of the logic implemented in other applications.

Whenever an action is taken that modifies the directory, I create the appropriate action and an actor, and then add the actor to the list of actors (as long as the actor does not already exist). I then execute the synchronous logic in the action object, and the asynchronous logic in the actor object. There is a 3 second sleep added for mimicking asynchronous operation as this program is more of an example for testing.

What I mostly would like input on is my synchronization and processing of async tasks. I am trying to maintain consistency by ensuring the file is not written to while another write operation is happening, but I also copied over the synchronization when creating actors from existing code. Is this a good approach?

```
public class UserDirectoryActor {

private final Action action;
private final ExecutorService pool = Executors.newFixedThreadPool(1);

public UserDirectoryActor(Action action) {
this.action = action;
}

public Action getAction() {
return action;
}

/*
* To simulate asynchronous processing, there is a 3 second wait
* between the start of the asynchronous operation and the
* actual writing of the user directory back to the file
*/
public Future executeAsynchronously() {
UserServiceFw.log.debug("Writing directory to file...");
Callable asyncTask = () -> {
UserServiceFw.entryManager.writeBack();
return "Finished!";
};
try {
action.execute();
TimeUnit.SECONDS.sleep(3);
UserServiceFw.log.debug("Finished writing directory to file!");
} catc

Solution

I would reshape your code in a way that any actor is an object that has a blocking queue as an inbox, a queue as an outbox and main loop (the run() method) that just queries the inbox, processes its stuff and writes to the outbox ad infinitum. Then you'd have a class that does the wiring for you: It would create all the actors, connect them through SynchronizedBlockingQueues and then add all actors to a thread pool of the correct size.

An interesting alternative to Actors could be using a Disruptor. This is a framework for throwing data around between threads and managing parallel processing and batching, which from the programmer's perspective, feels quite similar to actors. Their web site has pretty good tutorials and you'll find it easy to work with. The basic concept of a Disruptor is a ring buffer that has slots for data/events, there can only one writer to the ring buffer, but multiple readers (you might like to call them "event processors"). They are organized in a way that they can follow (and race) each other, but are guaranteed to to the jobs in the defined order. The ring buffer is sort of a common queue, but it's a bit more sophisticated than that. Pretty ingenious stuff and a lot of work and analysis went into this. It was used to implement the asynchronous loggers in Log4j2.

You might like to read my blog post about actors, where you can see an example of an actor model, implemented in plain java, including the pitfalls.

Context

StackExchange Code Review Q#136776, answer score: 2

Revisions (0)

No revisions yet.