在Android开发中线程的使用肯定是少不了的,因为在主线程是不能做耗时操作的;但是使用线程也不能随意的直接通过new Thrad方式去使用,因为像列表这种页面,有很多的图片需要加载,只能异步执行,如果直接new,那对手机内存的压力可想而知;好在Android给我们提供了线程池这样一个东西用来管理线程的操作;话说Android给我们提供了AsyncTask这样一个类,它内部维护了一个线程池,但是很蛋疼的是这个线程池是串行执行的,做不到并发操作,既然这样我们只有自己来封装下了
本文所含代码随时更新,可从这里下载最新代码
传送门
Android中关于线程的API其实Java的,老祖宗是Executor这样一个接口,提供了一个执行线程的方法execute(Runnable command);还有另外一个接口ExecutorService继承自Executor,扩展了它的能力
不能总是接口啊,总要有一个人来实现这些盖世武功吧,没错,就是ThreadPoolExecutor这孙子了(其实它还有一个兄弟叫ForkJoinPool,也实现了这些功能),这就是今天的主角线程池的代表
大家伙应该知道Java还提供了一个Executors类,可以通过它来构建多种不同的线程池
别看这些花里胡哨的方法,其实它们内部都是通过如下这种类似模式来创建线程池
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
这下明白了吧,Executors类只是官方为了开发者方便对线程池进行了下封装,所以要想做一个适合自己APP的线程池,那就自己动手封装一个线程池吧(其实延迟定时执行任务我们可以通过ThreadPoolExecutor+Handler做到)
ThreadPoolExecutor提供了四个重载的构造方法
这里以参数最多的构造方法为例对里面的参数进行讲解
了解了构造方法后,那么一个线程池的通用运行逻辑是什么样的呢?也就是线程池里的线程执行逻辑是啥?
当我们通过execute方法提交一个线程后,它的去向如下几种(这里排除核心线程数为0的情况)
了解了上面这些知识点后,我们就可以来做一个符合我们自己APP需求的线程池了
首先要做的是确定线程池的核心线程的数量和最大线程数量
还有一个就是要传入一个handler,应对线程池拒绝的情况,这样我们就可以定义一个线程池了,如下
/**
* @Description TODO(全局使用的线程池)
* @author cxy
* @Date 2018/11/14 17:22
*/
public class LocalThreadPools {
private static String TAG = LocalThreadPools.class.getSimpleName();
private static ExecutorService THREAD_POOL_EXECUTOR;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT-1,4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2;
private static final int KEEP_ALIVE_SECONDS = 60;
private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue<>(8);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "MangoTask #" + mCount.getAndIncrement());
}
};
private void initThreadPool() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory,new RejectedHandler()){
@Override
public void execute(Runnable command) {
super.execute(command);
Log.e(TAG,"ActiveCount="+getActiveCount());
Log.e(TAG,"PoolSize="+getPoolSize());
Log.e(TAG,"Queue="+getQueue().size());
}
};
//允许核心线程空闲超时时被回收
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
private class RejectedHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//可在这里做一些提示用户的操作
Toast.makeText(mContext.get(),"当前执行的任务过多,请稍后再试",Toast.LENGTH_SHORT).show();
}
}
private WeakReference mContext;
private static LocalThreadPools instance;
private LocalThreadPools(Context context){
mContext = new WeakReference<>(context);
initThreadPool();
}
public static LocalThreadPools getInstance(Context context){
if (instance == null) {
instance = new LocalThreadPools(context);
}
return instance;
}
public void execute(Runnable command){
THREAD_POOL_EXECUTOR.execute(command);
}
}
调用方法如下
LocalThreadPools.getInstance(this).execute(new Runnable() {
@Override
public void run() {
Log.e(TAG,"NAME="+Thread.currentThread().getName());
}
});
既然有创建线程池的方法,那肯定有关闭咯,如下
/**
* 通过interrupt方法尝试停止正在执行的任务,但是不保证真的终止正在执行的任务
* 停止队列中处于等待的任务的执行
* 不再接收新的任务
* @return 等待执行的任务列表
*/
public List shutdownNow(){
return THREAD_POOL_EXECUTOR.shutdownNow();
}
/**
* 停止队列中处于等待的任务
* 不再接收新的任务
* 已经执行的任务会继续执行
* 如果任务已经执行完了没有必要再调用这个方法
*/
public void shutDown(){
THREAD_POOL_EXECUTOR.shutdown();
sPoolWorkQueue.clear();
}