ImageLoader源码解析(三) 线程调度

ImageLoader源码解析(三) 线程调度

1 默认实现

iamgloader提供了一个默认线程池实现

  • DefaultConfigurationFactory.createTaskDistributor
  /**
     * 创建默认的 ThreadPoolExecutor
     * Creates default implementation of task distributor
     */
    public static Executor createTaskDistributor() {
        return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
    }
其实就一行代码,创建一个缓存线程池,传入一个TreadFactory 先看一下newCachedThreadPool这个方法
  public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
看一下这个方法的参数注解 - ThreadPoolExecutor
/**
* corePoolSize: 线程池维护线程的最少数量

* maximumPoolSize:线程池维护线程的最大数量

* keepAliveTime: 线程池维护线程所允许的空闲时间

* unit: 线程池维护线程所允许的空闲时间的单位

* workQueue: 线程池所使用的缓冲队列

* handler: 线程池对拒绝任务的处理策略
*/
ThreadPoolExecutor (int corePoolSize, 
                int maximumPoolSize, 
                long keepAliveTime, 
                TimeUnit unit, 
                BlockingQueue<Runnable> workQueue, 
                ThreadFactory threadFactory)
由上可以看出,咱们其实就是创建了一个 最小为0,最大为int最大值大小的同步队列的线程池 这里有一个类,就是ThreadFactory,咱们看一下这个类 先看一下接口 - ThreadFactory
public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}
通过注释和方法名就看的出来,通过怎么样的方式生成一个线程,可以做一些特定的初始化 - DefaultConfigurationFactory.DefaultThreadFactory

 /**
     * 线程工厂类
     */
    private static class DefaultThreadFactory implements ThreadFactory {
        //AtomicInteger,一个提供原子操作的Integer的类。
        // 在Java语言中,++i和i++操作并不是线程安全的,
        // 在使用的时候,不可避免的会用到synchronized关键字。
        // 而AtomicInteger则通过一种线程安全的加减操作接口。
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        /**
         * 线程组
         */
        private final ThreadGroup group;

        private final AtomicInteger threadNumber = new AtomicInteger(1);
        /**
         * 线程名字前缀
         */
        private final String namePrefix;
        /**
         * 线程级别
         */
        private final int threadPriority;

        /**
         * 初始化配置
         * @param threadPriority
         * @param threadNamePrefix
         */
        DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
            this.threadPriority = threadPriority;
            group = Thread.currentThread().getThreadGroup();
            namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            /**
             * 根据配置生成一个线程
             */
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            //是否是守护线程,设置为false
            if (t.isDaemon()) t.setDaemon(false);
            t.setPriority(threadPriority);
            return t;
        }
    }
其实上面无非就是 对线程做了一系列的初始化操作 这就是默认的实现了 ### 2 自定义缓存线程池的实现 看过默认实现,貌似也不是很难,就是对线程做下处理,构建一个线程池就OK了 那么自定义配置与默认有哪些区别呢,,自定义其实可以设置的属性包含以下3个方面 1 线程优先级 2 线程池实现队列 3 线程池的大小 其中优先级和大小很容易就能够控制,最复杂的是 队列的实现,Imagloaer提供了两种队列来处理线程池,LILO和LIFO,其实就是链表和栈的概念 #### 2.1 基础知识点 这里面涉及到几个类比较重要 - Collection接口,继承Iterator(迭代器)接口,这个大家应该熟悉,List就是实现了这个接口,集合概念 - Queue队列类,实现Collection接口,方法我来注释一下吧
public interface Queue<E> extends Collection<E> {
    /**
     * 插入元素,如果不可以,抛异常
     */
    boolean add(E e);

    /**
    * 插入一个元素,如果不可以,返回false,否则true
     */
    boolean offer(E e);

    /**
    * 与poll唯一的区别是,空队列抛异常
     */
    E remove();

    /**
     * 检索并移除头结点,空队列返回null
     */
    E poll();

    /**
    * 与peek唯一的区别是,如果这个队列是空的,抛出异常
     */
    E element();

    /**
     * 检索头结点,如果队列为空,返回null
     */
    E peek();
}
- Deque 双端队列,相较于Queue,加入了一些前后端操作(addFirst之类的)和Iterator迭代器的操作 - BlockingQueue和BlockingDeque,阻塞式队列和阻塞式双端队列,与Queue和Deque的不同就是在一些情况下,运行线程等待 拿BlockingQueue举例:如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作 参考:http://blog.csdn.net/caomiao2006/article/details/46676205 http://blog.csdn.net/longeremmy/article/details/8225989 http://www.cnblogs.com/skywang12345/p/3503480.html 能力有限,只能写这么多了,下面分析一下ImageLoader中的实现吧 #### 2.2Imagloader中的实现

QQ截图20170610151116.png

这是Imageloader中提供的队列类图
可以看到 LIFOLinkedBlockingDeque 是集成LinkedBlockingDeque的,我们去看一下代码

  • LIFOLinkedBlockingDeque
public class LIFOLinkedBlockingDeque<T> extends LinkedBlockingDeque<T> {

    private static final long serialVersionUID = -4114786347960826192L;

    @Override
    public boolean offer(T e) {
        return super.offerFirst(e);
    }

    @Override
    public T remove() {
        return super.removeFirst();
    }
}

可以看到,它只是重写了两个方法,做的事情就是 添加到队列顶端,删除也是删除顶端的节点,栈的概念

下面再看LinkedBlockingDeque
- LinkedBlockingDeque


public class LinkedBlockingDeque<E>
        extends AbstractQueue<E>
        implements BlockingDeque<E>, java.io.Serializable {

    /**
     * 双端队列(链表) 节点类
     */
    static final class Node<E> {
        /**
         * 元素,移出后为null
         */
        E item;

        /**
         * 前置节点,
         * 空代表没有前置节点,
         * 如果是自己,则意味着只有一个元素,自己的前置节点是自己
         */
        Node<E> prev;

        /**
         * 后置节点,与前置节点定义类似
         */
        Node<E> next;

        /**
         * 构造函数
         *
         * @param x
         */
        Node(E x) {
            item = x;
        }
    }

    /**
     * 第一个节点
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     * (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * 最后一个节点
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     * (last.next == null && last.item != null)
     */
    transient Node<E> last;

    /**
     * 队列元素数量
     * Number of items in the deque
     */
    private transient int count;

    /**
     * 队列容量
     * Maximum number of items in the deque
     */
    private final int capacity;

    /**
     * 锁,详情百度
     * Main lock guarding all access
     */
    final ReentrantLock lock = new ReentrantLock();

    /**
     * 空队列 阻塞条件,针对ReentrantLock
     */
    private final Condition notEmpty = lock.newCondition();

    /**
     * 非满 队列阻塞条件,针对ReentrantLock
     * 如果有线程想要插入一个元素,但是队列节点达到最大值,则线程等待
     * 如果这个时候移出了一个元素,需要用这个锁唤醒正在等待的那个线程
     */
    private final Condition notFull = lock.newCondition();

    /**
     * Creates a {@code LinkedBlockingDeque} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingDeque() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity.
     *
     * @param capacity 队列容量,小于0抛异常
     * @throws IllegalArgumentException if {@code capacity} is less than 1
     */
    public LinkedBlockingDeque(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
    }

    /**
     * 用一个集合来初始化队列,把元素放入到队列中,不可以则抛异常
     */
    public LinkedBlockingDeque(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock lock = this.lock;
        lock.lock(); // Never contended, but necessary for visibility
        try {
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (!linkLast(new Node<E>(e)))
                    throw new IllegalStateException("Deque full");
            }
        } finally {
            lock.unlock();
        }
    }

    //以下为 基本的链接和链接操作,只有持有锁的时候才可以做

    /**
     * 将一个节点设置为第一个节点,如果node为空,返回false
     */
    private boolean linkFirst(Node<E> node) {
        // assert lock.isHeldByCurrentThread();
        if (count >= capacity)
            return false;
        Node<E> f = first;
        node.next = f;
        first = node;
        if (last == null)
            last = node;
        else
            f.prev = node;
        ++count;
        notEmpty.signal();
        return true;
    }

    /**
     * 设置一个节点为最后一个节点
     */
    private boolean linkLast(Node<E> node) {
        // assert lock.isHeldByCurrentThread();
        if (count >= capacity)
            return false;
        Node<E> l = last;
        node.prev = l;
        last = node;
        if (first == null)
            first = node;
        else
            l.next = node;
        ++count;
        notEmpty.signal();
        return true;
    }

    /**
     * 移出并且返回第一个节点,如果为空返回null
     * 如果有第二个节点,第一个节点指向第二个节点
     * 唤醒正在等待的线程(如果有)
     */
    private E unlinkFirst() {
        // assert lock.isHeldByCurrentThread();
        Node<E> f = first;
        if (f == null)
            return null;
        Node<E> n = f.next;
        E item = f.item;
        f.item = null;
        f.next = f; // help GC
        first = n;
        if (n == null)
            last = null;
        else
            n.prev = null;
        --count;
        //唤醒一个正在等待的线程
        notFull.signal();
        return item;
    }

    /**
     * Removes and returns last element, or null if empty.
     */
    private E unlinkLast() {
        // assert lock.isHeldByCurrentThread();
        Node<E> l = last;
        if (l == null)
            return null;
        Node<E> p = l.prev;
        E item = l.item;
        l.item = null;
        l.prev = l; // help GC
        last = p;
        if (p == null)
            first = null;
        else
            p.next = null;
        --count;
        notFull.signal();
        return item;
    }

    /**
     * 移出一个节点
     * Unlinks x.
     */
    void unlink(Node<E> x) {
        // assert lock.isHeldByCurrentThread();
        Node<E> p = x.prev;
        Node<E> n = x.next;
        if (p == null) {
            unlinkFirst();
        } else if (n == null) {
            unlinkLast();
        } else {
            p.next = n;
            n.prev = p;
            x.item = null;
            // Don't mess with x's links.  They may still be in use by
            // an iterator.
            --count;
            notFull.signal();
        }
    }

    // 以下是 BlockingDeque中的操作的方法
    ....
    //以下是对一些操作的支持
    ....
    }

上面记录了一下基本操作,重写的BlockingDeque中的方法,都是获得锁,然后操作,然后释放锁,就不写注释了,里面有些方法

在BlockingDeque,有对ReentrantLock的高级用法,我了解不深,提供一个连接,供大家参考

ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。

Condition
线程之间的通信。Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用。

3 知识点汇总

Imagloader已经解析的差不多了,还要一些浅显的我就没有写,基本上属于那种看 单词就知道什么意思的,这里汇总一下Imagloaer里面用的东西吧
- 设计模式
- Builder模式
- 单利模式
- 工厂模式,好吧,也算吧
- 抽象接口提供灵活性和可扩展性,这个貌似是三大原则之一,对不起,我一个原则的名字都没有记住

  • 基础知识
    • ReentrantLock和Condition
    • Executor和ThreadPoolExecutor以及ExecutorService和Runnable
    • LinkedBlockingDeque,Deque,Queue,Collection
    • Bitmap和Drawable(在display中体现的淋漓尽致,虽然我没有分析)

4 总结

真是不看源码不知道自己的基础有多薄弱,前段时间还想着看Android Framwork源码,实在是不知天高地厚

知识的积累,并不是一定非要搞的特别懂,至少有个印象,熟悉一下用法,知道应用场景,也许有一天,你忽然间就明白了,原来这么做是为了实现什么什么,这个类,原理是这个样子啊

5 解析项目源码

https://github.com/yizeliang/Android-Universal-Image-Loader

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值