前言
Volley 是个优秀的网络请求框架,它的设计决定了它适合多个并行的小规模的请求,比如大量的接口请求。今天我们把Volley身上所有的皮肉都剃掉,只保留一个骨架,这个骨架对于我们理解如何实现一个多线程分发器很有帮助,首先我们先来看看Volley的架构图:
从使用者这一侧,我们只需要一个RequestQueue来放入请求,一旦请求放入了队列,首先会判断是否需要缓存,如果说这个请求明确不要缓存,就直接进入NetworkQueue,如果没有明确不缓存,则进入CacheQueue(换句话说就是,非特殊指定的请求默认都会缓存)。CacheDispatcher拿到Request后进入Cache检查,如果未命中Cache,则CacheDispatcher会把这个Request移到NetworkQueue中。NetworkDispatcher则从NetworkQueue中取出Request,然后进行网络请求。两种Dispatcher通过ResponseDelivery将结果传递回Request的回调(Listener)中。
本文尝试把Volley的Dispatcher机制简化,以此学习一个通用的分发器,这是一个典型的生产者消费者问题,生产者和消费者不直接通信,而是通过阻塞队列的中间人,达到生产者和消费者之间解耦。对应上面的图,我们这里介绍的内容就是图中虚线框中的部分。
1.首先我们需要一个队列:
public class Queue {
// 这个阻塞队列是核心,这里给队列加上了优先级
private final PriorityBlockingQueue < Task > queue = new PriorityBlockingQueue < Task >();
private DispatchThread [] mDispatchers ;
public Queue ( int size ) {
// size就是我们消费者的数量,这里固定为size个,有些系统里可以动态增减
mDispatchers = new DispatchThread [ size ];
}
public void add ( Task task ) {
synchronized ( queue ) {
queue . add ( task );
}
}
public void start () {
stop ();
for ( int i = 0 ; i < mDispatchers . length ; i ++) {
// 将队列传给Worker线程
DispatchThread thread = new DispatchThread ( queue , i );
mDispatchers [ i ] = thread ;
thread . start ();
}
}
public void stop () {
}
}
这段代码中的queue是我们整个系统的核心部分,所有的其它组件都是围绕他来运作的,线程的阻塞也是通过这个queue里的阻塞特性来实现的。
2. 然后是我们的Worker线程:
public class DispatchThread extends Thread {
private BlockingQueue < Task > mQueue ;
private int mId ;
public DispatchThread ( BlockingQueue < Task > queue , int id ) {
this . mQueue = queue ; // 持有队列来获取数据
mId = id ;
}
@Override
public void run () {
while ( true ) {
try {
// 假如队列为空,则这个线程就把队列阻塞了,其它线程都需要等待。
Task task = mQueue . take ();
System . out . print ( "Hi, Im thread:" + mId );
task . hello ();
sleep ( 500 );
System . out . println ( "" );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
}
}
3. 最后就是我们的任务啦:
public class Task implements Comparable {
private int mId ;
public Task ( int i ) {
mId = i ;
}
public void hello (){
System . out . println ( "Hello Im task:" + mId );
}
@Override
public int compareTo ( Object o ) {
return 0 ;
}
}
Task 比较简单,可以根据业务改变。
4. 来我们测试一下:
public class Main {
public static void main ( String [] args ) {
Queue queue = new Queue ( 4 );
for ( int i = 0 ; i < 20 ; i ++) {
queue . add ( new Task ( i ));
}
queue . start ();
Scanner s = new Scanner ( System . in );
int a ;
while (( a = s . nextInt ()) != - 1 ) {
queue . add ( new Task ( a ));
}
System . exit ( 0 );
}
}
运行结果:
Hello Im task:0
Hello Im task:19
Hello Im task:18
Hello Im task:17
Hello Im task:16
Hello Im task:15
Hello Im task:14
Hello Im task:13
Hello Im task:12
Hello Im task:11
Hello Im task:10
Hello Im task:9
Hello Im task:8
Hello Im task:7
Hello Im task:5
Hello Im task:6
Hello Im task:4
Hello Im task:3
Hello Im task:1
Hello Im task:2
5. 总结
从上面的简化的例子我们可以学习到一个多线程并发的模式,这个模式可以解决大多数并发问题。而通过阻塞而不是轮询,可以降低CPU的消耗。