The two of main differences are:
• Futures are eager. As soon as they are created the work to produce the result is going to happen unless canceled. Obersvables on the other hand are lazy and only start the computation when subscribe() is called. For example:
Observable<T> getCache(Req)
Observable<T> origin(Req)
Func1<T, Observable<T>> putCache(Req)
concat(getCache(req), origin(req).flatMap(putCache(req)).take(1)
what will happen is that first the getCache, origin, flatMap Observables are built but only the getCache is subscribed to and if the result is there a value will be returned and the take(1) will tare down the computation before a request is sent to the origin. if the result wasn't in the cache an empty Observable completes and concat moves on to subscribing to flatMap that in turn subscribes to origin observable.
• Futures are synchronous there has to be a thread dedicated to polling isDone or block on get(). When subscribing to an Observable the onNext closure could execute on a different thread (even after the thread that setup the Observable chain is gone). If you don't want to the data producers thread to tied up with other code it is easy to schedule it on a different thread pool later without any changes to the API. For example lets say that putCache from the last example was taking too long and slowing process of returning the result.
concat(getCache(req), origin(req).doOnNext(t -> putCache(req).call(t).subscribeOn(Schedulers.io()).subscribe(/* fire and forget */)).take(1)
This will schedule the writing to the cache to a different thread and not block the processing of the result without having to change putCache().