ImageLoader源码解析(三) 线程调度
来源:互联网 发布:矩阵的1范数,2范数 编辑:程序博客网 时间:2024/05/21 19:28
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,咱们看一下这个类先看一下接口- ThreadFactorypublic 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/46676205http://blog.csdn.net/longeremmy/article/details/8225989http://www.cnblogs.com/skywang12345/p/3503480.html能力有限,只能写这么多了,下面分析一下ImageLoader中的实现吧#### 2.2Imagloader中的实现这是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 实现组合使用。
- ReentrantLock和Condition更多参考:
http://blog.csdn.net/vernonzheng/article/details/8288251
http://blog.csdn.net/u013159433/article/details/51407320
http://blog.csdn.net/u010142437/article/details/42495213(Condition 方法介绍)
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
- ImageLoader源码解析(三) 线程调度
- RxJava线程调度源码解析
- Universal-ImageLoader源码解析
- ImageLoader源码解析
- ImageLoader 源码解析
- ImageLoader源码解析(一)
- ImageLoader源码解析-----ImageLoader的结构
- ImageLoader使用及源码解析
- 三、ScheduledThreadPoolExecutor 支持周期调度执行线程池解析
- Android-Universal-Imageloader源码完全解析
- ImageLoader源码解析(二) 缓存实现
- ImageLoader 源码
- ImageLoader解析
- ImageLoader解析
- imageLoader解析
- ImageLoader源码解析(四) 补充 Displayer的实现
- 线程池源码解析
- 线程池源码解析
- 剑指offer——python实现(面试必备)
- JZOJ2017.08.20B组
- spring源码分析(web)--通过注解获取bean源码分析
- python实现-求int型数据在内存中存储时1的个数
- 利用 Python 练习数据挖掘(鸢尾花练习)中遇到的问题,以及解决方法。
- ImageLoader源码解析(三) 线程调度
- DiskLruCache 达到最大设置的缓存,清理的逻辑
- Eigen实例操作
- 快速排序
- Qt5.7关于Quazip的编译和使用
- 2018搜狐笔试 Kolakoski数列问题
- 2、flask第二站-请求与相应
- K:(1)算法时间复杂度的简介
- TYPEERROR: UNDEFINED IS NOT A FUNCTION解决方法