Ранее мы писали, что subscribeOn() задает поток, на котором происходит подписка, и имеет эффект от создания Observable и вниз по цепочке вызовов RxJava до первого observeOn().
Это поведение обусловлено реализацией оператора subscribeOn():

1. При вызове Observable.subscribeOn() создается объект класса ObservableSubscribeOn, который является наследником Observable и выступает в качестве враппера для оригинального observable.

2. Когда на observable вызывается метод subscribe(), вызов делегируется в абстрактный метод subscribeActual() класса Observable, который реализован в ObservableSubscribeOn.

3. В методе subscribeActual() вызывается scheduleDirect() на объекте типа Scheduler, который был передан аргументом в оператор subscribeOn().
Параметром метода scheduleDirect() передается Runnable, в котором вызывается source.subscribe(), где source – это оригинальный Observable.

Из такой реализации следует, что все что делает subscribeOn() – это создание класса-враппера, который делегирует вызов subscribe() на оригинальный Observable, со сменой треда на переданный шедулер.