Comparison of Asynchronous Data Loading in Java
ejf.ioRxJava sounds like a nice formalization of how I have been "doing it" with Guava's ListenableFuture and single threaded executors. I always declare up front how tasks are distributed across threads and don't rely on random scheduling from a queue. This also creates opportunities to avoid locking and migrating data between threads.
It's about as painless as it gets when you aren't bottlenecked on number of actor like things. I never am because I eat the pain of not blocking rather than asking a framework to do it for me (or fail at it).
Java 8 has CompletableFuture so you don't need to leave the JDK anymore, but the javadoc makes my eyes bleed compared to Guava's ListenableFuture. I do like that CompletableFuture doesn't use synchronized internally.
The thing you have to aware of with async is backpressure. You have now decoupled thread pool exhaustion from task creation allowing unbounded memory usage. And once you allow backpressure you need to start thinking a little bit harder about deadlocks due to bounded queues.
Eliminating cycles, ignoring bounds where cycles exist, or allowing errors/shedding can help, but it's the kind of thing that keeps me up a night.
That's interesting, I had not come across CompletableFuture yet, I'll need to dig in more, but the JavaDoc looks promising (if confusing). It sounds like RxJava may also handle the backpressure issue, where you provide threadpools for the execution and handling of an observable.
I haven't played with Guava, but I have used Otto a bit, and at one point wrote my own simple observer framework (which is slightly less work than implementing a Java Observer). As I wrote in the disclaimer, there are lots of ways of doing this. =)