支付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中线程池技术剖析的主要内容,如果未能解决你的问题,请参考以下文章

线程池处理用户请求的流程剖析

接入支付宝支付SDK

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

线程池原理剖析

线程池核心原理剖析

JDK1.7中的ThreadPoolExecutor源代码剖析