Rxjava2 blockingFirst blockingLast

851 阅读2分钟

blockingFirst只会获取第一个元素,可以给出默认值,但是会阻塞调用blockingFirst()方法的线程。

 int v = Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
            Thread.sleep(3000);
            emitter.onNext(1);
            emitter.onNext(2);
            emitter.onNext(3);
        }).subscribeOn(Schedulers.io()).blockingFirst();
        System.out.println(v);
  public final T blockingFirst() {
        BlockingFirstObserver<T> observer = new BlockingFirstObserver<T>();
        subscribe(observer);
        T v = observer.blockingGet(); //可能会被阻塞
        if (v != null) {
            return v;
        }
        throw new NoSuchElementException();
    }

调用blockingFirst()方法的线程一般都会被阻塞住。看看代码是否如被阻塞的。

public final class BlockingFirstObserver<T> extends BlockingBaseObserver<T> {

    @Override
    public void onNext(T t) {
        if (value == null) {
            value = t;
            upstream.dispose();
            countDown(); //计数值减1,唤醒被阻塞的线程
        }
    }

    @Override
    public void onError(Throwable t) {
        if (value == null) {
            error = t;
        }
        countDown();
    }
}

这里的value初始化的时候就是null,一旦被赋值,被阻塞的线程就会被唤醒。

如果熟悉CountDownLatch的就不需要看这段话了。 CountDownLatch类似与计数器,CountDownLatch有2个重要的方法awaitcountDown方法,调用await会被阻塞,调用countDown计数值会被减去1,一直到0就会唤醒被阻塞的线程,CountDownLatch经常用在一个线程需要等待其他线程都做完活后,自己才能做事,比如多任务下载文件,A线程下载A段,B线程下载B段,C线程下载C段,D线程需要将下载好的视频复制到目录,首先初始化CountDownLatch,计数值设置3,D线程调用await被阻塞,A线程下完后调用countDown,计数值减1,B线程下完后调用countDown,计数值减1,C线程下完后调用countDown,计数值减1,现在是计数值是0,D线程被唤醒,开始自己的任务。

public abstract class BlockingBaseObserver<T> extends CountDownLatch
implements Observer<T>, Disposable {

    T value;
    Throwable error;

    Disposable upstream;

    volatile boolean cancelled;

    public BlockingBaseObserver() {
        super(1);//设置计数值
    }

    @Override
    public final void onSubscribe(Disposable d) {
        this.upstream = d;
        if (cancelled) {
            d.dispose();
        }
    }

    @Override
    public final void onComplete() {
        countDown();
    }

    @Override
    public final void dispose() {
        cancelled = true;
        Disposable d = this.upstream;
        if (d != null) {
            d.dispose();
        }
    }

    @Override
    public final boolean isDisposed() {
        return cancelled;
    }

   //调用该方法的线程被阻塞
    public final T blockingGet() {
        if (getCount() != 0) { 
            try {
                BlockingHelper.verifyNonBlocking();
                await(); //阻塞,等着计数值减为0
            } catch (InterruptedException ex) {
                dispose();
                throw ExceptionHelper.wrapOrThrow(ex);
            }
        }

        Throwable e = error;
        if (e != null) {
            throw ExceptionHelper.wrapOrThrow(e);
        }
        return value;
    }
}

第一次执行到onNext方法,value被赋值,执行 countDown()方法,计数值为0,唤醒阻塞线程,拿到value。

blockingLast只会获取最后一个元素。

 public final T blockingLast() {
        BlockingLastObserver<T> observer = new BlockingLastObserver<T>();
        subscribe(observer);
        T v = observer.blockingGet(); //被阻塞
        if (v != null) {
            return v;
        }
        throw new NoSuchElementException();
    }
public final class BlockingLastObserver<T> extends BlockingBaseObserver<T> {

    @Override
    public void onNext(T t) {
        value = t; //每次调用,更新此值
    }

    @Override
    public void onError(Throwable t) {
        value = null;
        error = t;
        countDown();
    }
}

BlockingLastObserver每次更新最新的值,那在那里调用 countDown()方法? 在BlockingBaseObserver里由默认的onComplete方法内部,就执行了 countDown()方法。