美文网首页
源码翻译师1 Android-Lite-Go(Part2)

源码翻译师1 Android-Lite-Go(Part2)

作者: 小石头_Kerry | 来源:发表于2016-03-28 17:39 被阅读0次

(2)Java Executor框架
参考:
Java Executor框架

Executor框架是指java5中引入的一系列并发库中与executor相关的功能类,包括Executor、Executors、ExecutorService、CompletionService、Future、Callable等。通过学习Executor框架能够更加清楚明晰作者的写作思路和来源。


Class Executor

开始的开始 Executor

public interface Executor {
    void execute(Runnable command); 
}

Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法。它没有直接的实现类,有一个重要的子接口ExecutorService。

ExecutorService
ExecutorService接口继承自Executor接口,定义了终止、提交任务、跟踪任务返回结果等方法。

Runnable、Callable、Future

// 实现Runnable接口的类将被Thread执行,表示一个基本的任务
public interface Runnable {
    // run方法就是它所有的内容,就是实际执行的任务
    public abstract void run();
}

// Callable同样是任务,与Runnable接口的区别在于它接收泛型,同时它执行任务后带有返回内容
public interface Callable<V> {
    // 相对于run方法的带有返回值的call方法
    V call() throws Exception;
}

// Future代表异步任务的执行结果
public interface Future<V> {

    /**
     * 尝试取消一个任务,如果这个任务不能被取消(通常是因为已经执行完了),返回false,否则返回true。
     */
    boolean cancel(boolean mayInterruptIfRunning);

    /**
     * 返回代表的任务是否在完成之前被取消了
     */
    boolean isCancelled();

    /**
     * 如果任务已经完成,返回true
     */
    boolean isDone();

    /**
     * 获取异步任务的执行结果(如果任务没执行完将等待)
     */
    V get() throws InterruptedException, ExecutionException;

    /**
     * 获取异步任务的执行结果(有最常等待时间的限制)
     *
     *  timeout表示等待的时间,unit是它时间单位
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

ScheduledFuture继承自Future和Delayed接口,自身没有添加方法。Delayed接口定义了一个获取剩余延迟的方法。

ExecutorService有一个子接口ScheduledExecutorService和一个抽象实现类AbstractExecutorService。

ScheduledExecutorService
可以安排指定时间或周期性的执行任务的ExecutorService

// 可以安排指定时间或周期性的执行任务的ExecutorService
public interface ScheduledExecutorService extends ExecutorService {
    /**
     * 在指定延迟后执行一个任务,只执行一次
     */
    public ScheduledFuture<?> schedule(Runnable command,
                       long delay, TimeUnit unit);
    /**
     * 与上面的方法相同,只是接受的是Callable任务
     */
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                       long delay, TimeUnit unit);
    /**
     * 创建并执行一个周期性的任务,在initialDelay延迟后每间隔period个单位执行一次,时间单位都是unit
     * 每次执行任务的时间点是initialDelay, initialDelay+period, initialDelay + 2 * period...
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                          long initialDelay,
                          long period,
                          TimeUnit unit);
    /**
     * 创建并执行一个周期性的任务,在initialDelay延迟后开始执行,在执行结束后再延迟delay个单位开始执行下一次任务,时间单位都是unit
     * 每次执行任务的时间点是initialDelay, initialDelay+(任务运行时间+delay), initialDelay + 2 * (任务运行时间+delay)...
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                             long initialDelay,
                             long delay,
                             TimeUnit unit);
}

AbstractExecutorService
看到AbstractExecutorService的时候,基本就能找到一点痕迹了,LiteGo的核心代码有一些都参照了它的写法。
我们先来了解下下面三个概念:

  • RunnableFuture继承自Future和Runnable,只有一个run()方法。RunnableFuture接口看上去就像是Future和Runnable两个接口的组合。
public interface RunnableFuture<V> extends Runnable, Future<V> {    
        void run();
}
  • FutureTask类实现了RunnableFuture接口,除了实现了Future和Runnable中的方法外,它还有自己的方法。
  • ExecutorCompletionService实现了CompletionService接口,将结果从复杂的一部分物种解耦出来。

ThreadPoolExecutor
ThreadPoolExecutor继承自AbstractExecutorService。

ScheduledThreadPoolExecutor
它继承自ThreadPoolExecutor并实现了ScheduledExecutorService接口。

Executors
Executors中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory
和 Callable
类的工厂和实用方法。

  • newFixedThreadPool
    创建一个定长的线程池。达到最大线程数后,线程数不再增长。
    如果一个线程由于非预期Exception而结束,线程池会补充一个新的线程。
  • newCachedThreadPool
    创建一个可缓存的线程池。当池长度超过处理需求时,可以回收空闲的线程。
  • newSingleThreadPool
    创建一个单线程executor。
  • newScheduledThreadPool
    创建一个定长的线程池,而且支持定时的以及周期性的任务执行。
    类似于Timer。但是,Timer是基于绝对时间,对系统时钟的改变是敏感的,而ScheduledThreadPoolExecutor只支持相对时间。

与Timer比较

  1. Timer是创建唯一的线程来执行所有的timer任务。如果一个任务超时了,会导致其他的TimerTask时间准确性出问题。
  2. 如果TimerTask抛出uncheck 异常,Timer将会产生无法预料的行为。因此,ScheduledThreadPoolExecutor可以完全代替Timer。

(3)LinkedList 和 ArrayList
参考链接
因为平时我们大多使用的是动态数组ArrayList,对LinkedList并不是那么的熟悉,这里探究一下它们的区别和源码中为什么要使用LinkedList。

LinkedList和ArrayList都实现了List接口,但是它们的工作原理却不一样。它们之间最主要的区别在于ArrayList是可改变大小的数组,而LinkedList是双向链接串列(doubly LinkedList)。

  • 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
  • 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
  • 类似于插入数据,删除数据时,LinkedList也优于ArrayList。
  • LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
    在LinkedList中有一个私有的内部类,定义如下:
private static class Entry { 
         Object element; 
         Entry next; 
         Entry previous; 
     } 

这也就是LinkedList耗费内存更多的地方。

什么场景下更适宜使用LinkedList,而不用ArrayList

  • 你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。
  • 你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。

以上就是关于ArrayList和LinkedList的差别。你需要一个不同步的基于索引的数据访问时,请尽量使用ArrayList。ArrayList很快,也很容易使用。但是要记得要给定一个合适的初始大小,尽可能的减少更改数组的大小。

由此可见,LiteGo的使用场景中可能会出现大量的任务插入和删除(插入和删除有可能发生在列表的前面),但并不会涉及到查询,所以这里使用LinkedList是有道理的。

相关文章

网友评论

      本文标题:源码翻译师1 Android-Lite-Go(Part2)

      本文链接:https://www.haomeiwen.com/subject/pvvjlttx.html