支付SDK中线程池技术剖析
Posted Fastpay快付
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了支付SDK中线程池技术剖析相关的知识,希望对你有一定的参考价值。
每当我们启动一个应用那一刻起,就与线程有千丝万缕的联系,我们使用DDMS调试某一个应用时,可以发现会启动很多个线程,如下图所示:
通过对上面这个图进行整理和分类,如下所示大致可以分成以下:
main(主线程),也叫UI线程,Main入口函数所在的线程为主线程。系统启动一个应用时,会把主线程命名为“main”,在android里面主线程负责界面的绘制和将事件分发到各个View或其它组件。在主线程里面做耗时操作(文件操作、网络通讯等)需要特别小心,容易导致界面卡顿出现ANR(Application NotResponding)情况,所以需要将耗时操作放在其它工作线程中,最简单的是使用AsyncTask类,它会帮助你启动新的工作线程,并让返回的结果与用户界面进行交互。
GC(gc线程)
GC (gc线程)
这里输入标题
GC线程也叫回收线程,是Dalvik虚拟机启动的过程中创建的,它的执行体函数是gcDaemonThread,虚拟机支持非并行和并行两种GC,回收过程是一个非常复杂的过程,它要将那些不再被引用的对象进行回收,网上有很多讲述虚拟机GC方面的内容,可以直接搜索GC。其功能主要包含标记出进程内哪些对象是不再被引用的,并在恰当时机地回收堆上面的内存,避免应用程序长时间停顿和提升内存运行效率。
signal catcher(捕获linux信号)
此线程负责接收和处理Linux kernel发送的各种信号,例如SIGNAL_QUIT、SIGNAL_USR1等就是被该线程接收到,例如,崩溃或异常退出时, CPU通过异常中断的方式,此线程捕获到信号数据将触发异常处理流程。Native崩溃堆栈里面都会包含以下内容(signal 11 (Address not mapped to object) at address 0x0)
JDWP(android ddms相关)
JDWP(java debug wireprotocol)是dalvik VM的一个线程,使用LocalSocket组件建立在adb或者tcp基础上,与DDMS工具或debugger进行通信。
compiler
Dalvik JIT编译线程名称后面标识有daemon,这是个守护线程,负责字节码与机器码之间的编译、运行和优化工作。
ReferenceQueueDaemon
堆管理相关功能的守护线程,负责引用队列的守护。我们知道,在创建引用对象的时候,可以关联一个对象队列中,当被引用对象引用的对象不再被需要并被GC回收的时候,被引用对象就会被加入到其创建时关联的队列去。这个加入队列的操作就是由这个线程来完成的。这样应用程序就可以知道哪些被引用的对象已经被回收了。
FinalizerDaemon(执行finalize的守护线程)
执行析构工作的守护线程。对于重写了成员函数finalize的对象,它们被GC决定回收时,并没有马上被回收,而是被放入到一个队列中,等待FinalizerDaemon守护线程去调用它们的成员函数finalize,然后再被回收。
FinalizerWatchdogDaemon(监控finalize执行时间的守护线程)
析构监护守护线程。用来监控FinalizerDaemon线程的执行。一旦检测那些重定了成员函数finalize的对象在执行成员函数finalize时超出一定的时候,那么就会退出虚拟机。
Binder_1(binder线程)
Binder线程是进程的线程池中用来处理IPCbinder请求的线程
WifiManager(系统wifi管理线程)
负责启动和关闭Wifi设备相关的wpa_supplicant程序, 包含把命令下发给wpa_supplicant以及更新WIFI的状态。
CookieSyncManager(webview cookie相关线程)
负责WebView组件中的Cookie管理工作。
AsyncTask(asynctask线程)
AsyncTask的设计目的是为了方便开发者能够更方便地将工作线程和UI线程集成在一起配合完成业务。在Sdk应用场景中一般用于网络请求或文件处理后的界面更新和响应。下面重点介绍一下使用方法及需要注意地方。
在我们业务开发过程中经常会面临一种场景,往往会把复杂的、耗时业务逻辑(IO操作,网络请求)封装在一些doXXX方法中。然后启动一个工作线程去调用doXXX方法,做完函数里面的事项之后,需要将结果显示在UI层,这时就可以考虑使用AsyncTask。
private static JobAsyncTask<Result> extends AsyncTask<Object, Integer, Result> {
private ICallback<Result> mUICallback = null;
public JobAsyncTask(ICallback<Result> uiCallback){
mUICallback = uiCallback;
}
@Override
protected Result doInBackground(Object... params){
// TODO 这里是耗时操作
return result;
}
@Override
protected void onPreExecute(){
super.onPreExecute();
// TODO 这里是UI操作
mUiCallback.onPreExecute();
}
// 类似实现,这里省略....
}
这只是一个简单的使用,可以使用一些接口将业务与UI解耦,并配合AsyncTask.THREAD_POOL_EXECUTOR来使用可以收到很好的效果,不过需要关注的是api11以上才可以使用这个线程池的模式。
但事情都有两面性的,AsyncTask使用起来方便,同时也很多陷阱。如果在编码时没有注意,就很容易掉进这些坑里面。
第一. 存在对象被回收或引用失效问题
与界面相关肯定脱离不了Activity和Fragment等组件,但是Activity和Fragment等组件是有生命周期的,如果异步的工作还没有做完,而界面已经Finish了,这个时候再去操作界面就会出现问题,所以在Activity/Fragment的onDestory()中需要停止AsyncTask,否则会产生如下几个问题:
由于AsyncTask的实现类往往是Activity或Fragment的内部成员类,其隐式地保留了指向UI的引用。因此当Activity或Fragment被destory后,若AsyncTask仍然还在后台保持运行,会阻碍UI对象的内存回收,直至AsyncTask执行完毕或中止。解决办法是用static 的inner class,并持有Activity或Fragment的weak reference。
执行到onPostExecute()方法时,即使这个时候更新界面已经没有意义了。而且在onPostExecute中如果不做有效性检查而直接访问属于原Activity或Fragment的窗口控件,会造成访问异常导致崩溃,但这种问题不处理好,在自建的线程中也会出现问题。
第二,cancel可能无法真正中止
一般在Activity.ondestory时我们将会把未执行完的线程给中止或取消掉,我们发现AsyncTask的cancel()函数,正常情况,是可以被中止掉的,但存在一种异常情况,如果用户转动屏幕导致Activity被重新创建时,直接cancel掉AsyncTask会造成已完成的部分工作的浪费。从源代码可知,cancel(boolean)做的事情如下:
设置AsyncTask.mCancelled=true。
调用FutureTask.cancel(),最终会调用到Thread.interrupt()。
如果AsyncTask的doInBackGround()中未对isCancelled和interrupted做检查并主动退出,则后台任务不会终止。所以需要在doInBackground函数中判断这两个标识才会终止。
http-thread(应用使用的http线程池)
网络线程池一般都会联想到ThreadPoolExecutor,Executors,网络上有很多讲述这两个的使用方法,但如果需要使用到线程池时,不推荐使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写时候可以更加明确线程池的运行规则,规避资源耗尽的风险。
Executors返回的线程池对象的弊端如下:
FixedThreadPool或SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
CachedThreadPool或ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(10);
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES*2,
KEEP_ALIVE_TIME,
KEEP_ALIVE_TIME_UNIT,
taskQueue,
new BackgroundThreadFactory(),
new DefaultRejectedExecutionHandler());
以上是关于支付SDK中线程池技术剖析的主要内容,如果未能解决你的问题,请参考以下文章
newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段