RxJava之Scheduler (二)

1,142 阅读4分钟

1.SingleScheduler

SingleScheduler是RxJava2新增的Scheduler。SingleScheduler中有一个属性叫作executor,它是使用AtomicReference包装的ScheduledExecutorService。

补充:AtomicReference类的作用:AtomicReference则对应普通的对象引用,即保证你在修改对象引用时的线程安全性;对”对象”进行原子操作

final AtomicReference<ScheduledExecutorService> executor = new AtomicReference<ScheduledExecutorService>();

在SingleScheduler构造函数中,Executor会调用lazySet().

    /**
     * @param threadFactory thread factory to use for creating worker threads. Note that this takes precedence over any
     *                      system properties for configuring new thread creation. Cannot be null.
     */
    public SingleScheduler(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        executor.lazySet(createExecutor(threadFactory));
    }

************ 分割线 ************

其中**lazySet()**是AtomicReference中的方法,用于修改引用对象:

    // AtomicRefence类
    /**
     * Sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(V newValue) {
        value = newValue;
    }
    
    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(V newValue) {
        U.putOrderedObject(this, VALUE, newValue);
    }

AtomicReferences中set()和lazySet()区别:set()立刻修改旧值,别的线程可以立刻看到更新后的值;而lazySet()不会立刻(但是最终会)修改旧值,别的线程看到新值的时间会延迟一些。

************ 分割线 ************

它的createExecutor()用于创建工作线程,可以看到通过SchedulerPoolFactory来创建ScheduledExecutorService。

    // SingleScheduler类
    
    static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) {
        return SchedulerPoolFactory.create(threadFactory);
    }

通过SchedulerPoolFactory类的create(ThreadFactory factory)来创建单线程的线程

    // SchedulerPoolFactory类
    
    /**
     * Creates a ScheduledExecutorService with the given factory.
     * @param factory the thread factory
     * @return the ScheduledExecutorService
     */
    public static ScheduledExecutorService create(ThreadFactory factory) {
        // 创建单线程
        final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);
        
        if (exec instanceof ScheduledThreadPoolExecutor) {
            ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec;
            POOLS.put(e, exec);
        }
        return exec;
    }

在SingleScheduler中,每次使用ScheduledExecutorService时,其实是使用executor.get()。所以说,single拥有一个线程单例。

SingleScheduler会创建一个ScheduledWorker,ScheduledWorker使用JDK的ScheduledExecutorService作为executor。 下面是ScheduledWorker的schedule()方法,使用ScheduledExecutorService的submit()或schedule()来执行runnable。

        @NonNull
        @Override
        public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
            if (disposed) {
                return EmptyDisposable.INSTANCE;
            }

            Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

            ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, tasks);
            tasks.add(sr);

            try {
                Future<?> f;
                if (delay <= 0L) {
                    /**
                     * 立即执行则执行submit()方法
                     * Submits a value-returning task for execution and returns a
                     * Future representing the pending results of the task. The
                     * Future's {@code get} method will return the task's result upon
                     * successful completion.
                     */
                    f = executor.submit((Callable<Object>)sr);
                } else {
                
                    /**
                     * 需延迟执行,则执行schedule()方法
                     * Creates and executes a ScheduledFuture that becomes enabled after the
                     * given delay.
                     */
                    f = executor.schedule((Callable<Object>)sr, delay, unit);
                }

                sr.setFuture(f);
            } catch (RejectedExecutionException ex) {
                dispose();
                RxJavaPlugins.onError(ex);
                return EmptyDisposable.INSTANCE;
            }

            return sr;
        }

2.ComputationScheduler

ComputationScheduler使用FixedSchedulerPool作为线程池,并且FixedSchedulerPool被AtomicReference包装了一下。

从ComputationScheduler的源码中可以看出,MAX_THREADSCPU的数目FixedSchedulerPool可以理解为拥有固定数量的线程池(有点类似线程池中的FixedThreadPool),数量为MAX_THREADS。

    static {
        MAX_THREADS = cap(Runtime.getRuntime().availableProcessors(), Integer.getInteger(KEY_MAX_THREADS, 0));
        ...
    }
    
    static int cap(int cpuCount, int paramThreads) {
        return paramThreads <= 0 || paramThreads > cpuCount ? cpuCount : paramThreads;
    }

ComputationScheduler类会创建一个EventLoopWorker。

    @NonNull
    @Override
    public Worker createWorker() {
        return new EventLoopWorker(pool.get().getEventLoop());
    }

其中getEventLoop()是FixedSchedulerPool中的方法,返回了FixedSchedulerPool中的一个PoolWorker。

注:FixedSchedulerPoolEventLoopWorker都为ComputationScheduler的内部类

        // EventLoopWorker类中的方法
        
        public PoolWorker getEventLoop() {
            int c = cores;
            if (c == 0) {
                return SHUTDOWN_WORKER;
            }
            // simple round robin, improvements to come
            return eventLoops[(int)(n++ % c)];
        }

PoolWorker继承自NewThreadWorker,也是线程数为1的ScheduledExecutorService。

3.IoScheduler

IoScheduler使用CachedWorkerPool作为线程池,并且CacheWorkerPool也被AtomicReference包装了一下。 CachedWorkerPool是基于RxThreadFactory这个ThreadFactory来创建的。

    static {
        ...
        WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
        ...
        NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
        ...
    }

在RxThreadFactory中,由prefix和incrementAndGet()来创建新线程的名称

    @Override
    public Thread newThread(Runnable r) {
        StringBuilder nameBuilder = new StringBuilder(prefix).append('-').append(incrementAndGet());

        String name = nameBuilder.toString();
        Thread t = nonBlocking ? new RxCustomThread(r, name) : new Thread(r, name);
        t.setPriority(priority);
        t.setDaemon(true);
        return t;
    }

IoScheduler创建的线程数是不固定的,可以通过IOScheduler的size()来获取当前的线程数。一般情况下,ComputationScheduler的线程数等于CPU的数目

    public int size() {
        return pool.get().allWorkers.size();
    }

注意:ComputationSchedulerIoScheduler都是依赖线程池来维护线程的,区别在于:IoScheduler线程池中的个数是无限的,由prefix和incrementAndGet()产生的递增值来决定线程的名字。而ComputationScheduler中则是一个固定线程数量的线程池,数量为CPU的数目,并且不要把I/O操作放在computation()中,否则I/O操作的等待时间会浪费CPU

同样,IoScheduler也会创建EventLoopWorker类

    public Worker createWorker() {
        return new EventLoopWorker(pool.get());
    }

但这个EventLoopWorker是IoScheduler的内部类,与ComputationScheduler创建的EventLoopWorker不同,知识二者同名罢了,且都是继承Scheduler.WOrker类而已。

4.NewThreadScheduler

NewThrScheduler会创建NewThreadWorker,NewThreadWorker的构成函数使用的也是SchedulerPoolFactory。

    public NewThreadWorker(ThreadFactory threadFactory) {
        executor = SchedulerPoolFactory.create(threadFactory);
    }

与SingleScheduler不同的是,SingleScheduler的executor是使用AtomicReference包装的SchedulerExecutorService。每次使用时,都会调用executor.get()。

然而,NewThreadScheduler每次都会创建一个新的线程

个人这块不太懂,按照自我理解分析下:在SingleScheduler因为该调度器中只有一个线程,因而在后续调用,需要保证该对象唯一且保证前后一致,因而使用AtomicReference保证其他线程能知道;而NewThreadScheduler中每次都会创建新的线程,因而无需保证线程同步,不用管是否让其他线程知道。

5.TrampolineScheduler

TrampolineScheduler会创建TrampolineWorker,在TrampolineWorker内部维护着一个PriorityBlockingQueue。任务进入该队列之前,会先用TimedRunnable封装下。

    static final class TimedRunnable implements Comparable<TimedRunnable> {
        final Runnable run;
        final long execTime;
        final int count; // In case if time between enqueueing took less than 1ms

        volatile boolean disposed;

        TimedRunnable(Runnable run, Long execTime, int count) {
            this.run = run;
            this.execTime = execTime;
            this.count = count;
        }

        @Override
        public int compareTo(TimedRunnable that) {
            int result = ObjectHelper.compare(execTime, that.execTime);
            if (result == 0) {
                return ObjectHelper.compare(count, that.count);
            }
            return result;
        }
    }

可以看到TimeRunnable实现了Comparable接口,会比较任务的execTime和count。 任务在进入queue之前,count每次都会+1.

    final TimedRunnable timedRunnable = new TimedRunnable(action, execTime, counter.incrementAndGet());
    queue.add(timedRunnable);

所以,在使用TrampolineScheduler时,新的任务总会优先执行