我们在设计下载框架的时候,有时候我想改变下载任务的下载顺序,这个时候,我们涉及到一个下载队列得优先级。
既然是作下载,安卓这边的操作,大体技术方案,可能就是 线程池+ Runnable + 管理分发任务的队列。
我们先不考虑 service后台服务这一层,线程池的类别。
既然要写一个多线程环境下的操作的队列,那么需要考虑同步互斥,安全等。先看下jdk 提供API,有 ArrayBlockingQueue,
ConcurrentLinkedDeque(双向链表结构),ConcurrentLinkedQueue, 以及 LinkedBlockingQueue,LinkedBlockingDueue, PriorityBlockingQueue。。。我因为链表结构上,逻辑增删快,查询慢。因为一个下载任务,可能被随时取消或者增加,我想业务上选择这个单向链表结构不错。
既然可以自定义下载优先级,那么我想,我应该定义一个优先级的队列,所以看下 jdk源码下的LinkedBlockingQueue是怎么实现的,我们先看put方法。LinkedBlockingQueue 持有2把 ReentrantLock 锁,以及 2个 Condition对象。
看看 LinkedBlockingQueue源码:
先看put 方法。
public void put(E e) throws InterruptedException {
//因为加入的节点 是空的,所以抛出异常,
if (e == null) throw new NullPointerException();
//c=-1 初始化一个变量
int c = -1;
//构造一个节点
Node<E> node = new Node<E>(e);
//取出 成员变量的锁
final ReentrantLock putLock = this.putLock;
// 定义一个 多线程安全的自增数
final AtomicInteger count = this.count;
//开始加一把锁,可能是多线程环境下,其他线程也执行到这儿,然后当
//前线程谁先得到执行权,就直接把锁锁上,确保 put,offer操作,都只又一个线程进行 添加元素的操作
putLock.lockInterruptibly();
// 这儿的Try finally 是 jdk API 标准code 格式,防止线程唤醒
try {
// 死循环判断是为了 防止假唤醒,jdk api 说的
while (count.get() == capacity) {
// 如果 相等于linkedBlockingQueue的大小了,就把这个信号量给等待,
//那么当前线程就等待,多个线程执行 其他的put ,offer也是等待状态
// not Full 为什么不可以再加入两个单词了, not full do addNode。没装满,还可以继续装的操作。等待
notFull.await();
}
//这里是加Node节点
enqueue(node);
//C 表示 加入元素前的变量
c = count.getAndIncrement();
// 如果 还有空间,notFull 信号量,表示可以让其他线程取抢 putLock锁的执行权了
if (c + 1 < capacity)
notFull.signal();
} finally {
//finally 当前运行线程,释放 putLock 锁,
putLock.unlock();
}
if (c == 0)
// 如果c为0 那就说明会有take,或者offer操作阻塞,so 唤醒
signalNotEmpty();
}