I am trying to wrap my head around how futures work under the hood. I am familiar with the concept in both Java and Scala. I've been using futures in PlayFramework to prevent blocking operations from occupying my connection threads. That has worked well for the most part, but I am still feeling that there are some parts under the hood that I am missing. Particularly, when it comes to keeping the number of threads used for executing blocking operations low.
My generation assumption is (correct me if I am wrong) that there should be a single thread (in the simplest case), running an endless loop over a collection of pending futures. On every turn, the thread sleeps a little, then picks the futures one by one, checks if some results have arrived, and if so, returns them and removes the finished futures form the collection.
IMHO, this can only work if the underlying operations are non-blocking too. Otherwise, logic tells me that those should be isolated in their own separate threads, as part of a pool. My train of thought crashes at the point where each operation, even within a future is fundamentally blocking. Then, I would assume that in the worst case scenario, we would once again end up with one thread per blocking operation, even when wrapped in futures.
The problem is that the a large portion of the widely used IO code in Java is fundamentally blocking. This means that executing 15 JDBC operations wrapped in futures, will still spun off 15 threads. Otherwise, we would have to call them sequentially on a single thread, which is even worse.
What I am trying to say is that wrapping fundamentally blocking IO operations in futures, should in theory not help at all. Am I right or wrong? Please, help me build the puzzle.